slate-angular 20.0.0 → 20.2.0-next.0

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.
@@ -2,7 +2,7 @@ import { Editor, Range, Element, Transforms, Text as Text$1, Node, Path } from '
2
2
  import { EDITOR_TO_ELEMENT, NODE_TO_ELEMENT, DOMEditor, normalizeDOMPoint, isDOMSelection, IS_CHROME as IS_CHROME$1, hasShadowRoot, isDOMElement, NODE_TO_PARENT, NODE_TO_INDEX, IS_FOCUSED, isDOMNode, withDOM, NODE_TO_KEY, ELEMENT_TO_NODE, getDefaultView, EDITOR_TO_WINDOW, IS_READ_ONLY, EDITOR_TO_ON_CHANGE, TRIPLE_CLICK, isPlainTextOnlyPaste } from 'slate-dom';
3
3
  import { isKeyHotkey } from 'is-hotkey';
4
4
  import * as i0 from '@angular/core';
5
- import { TemplateRef, ViewChild, Component, ComponentRef, IterableDiffers, inject, ViewContainerRef, forwardRef, HostBinding, Input, ChangeDetectionStrategy, NgModule, ElementRef, ChangeDetectorRef, Directive } from '@angular/core';
5
+ import { TemplateRef, ComponentRef, IterableDiffers, inject, ViewContainerRef, forwardRef, HostBinding, Input, ChangeDetectionStrategy, Component, NgModule, ElementRef, ChangeDetectorRef, Directive, ViewChild } from '@angular/core';
6
6
  import { direction } from 'direction';
7
7
  import scrollIntoView from 'scroll-into-view-if-needed';
8
8
  import { Subject } from 'rxjs';
@@ -470,6 +470,8 @@ const HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY &&
470
470
  globalThis.InputEvent &&
471
471
  // @ts-ignore The `getTargetRanges` property isn't recognized.
472
472
  typeof globalThis.InputEvent.prototype.getTargetRanges === 'function';
473
+ const VIRTUAL_SCROLL_DEFAULT_BUFFER_COUNT = 3;
474
+ const VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT = 40;
473
475
 
474
476
  /**
475
477
  * Hotkey mappings for each platform.
@@ -839,6 +841,9 @@ const getContentHeight = (element) => {
839
841
  }
840
842
  return Math.max(contentHeight, 0);
841
843
  };
844
+ const getZeroTextNode = () => {
845
+ return document.createTextNode('\uFEFF');
846
+ };
842
847
 
843
848
  const buildHTMLText = (wrapper, attach, data) => {
844
849
  const stringObj = JSON.stringify(data);
@@ -1512,38 +1517,12 @@ function restoreDom(editor, execute) {
1512
1517
  }, 0);
1513
1518
  }
1514
1519
 
1515
- class SlateBlockCard {
1516
- get nativeElement() {
1517
- return this.elementRef.nativeElement;
1518
- }
1519
- get centerContainerElement() {
1520
- return this.centerContainer.nativeElement;
1521
- }
1522
- constructor(elementRef) {
1523
- this.elementRef = elementRef;
1524
- }
1525
- ngOnInit() {
1526
- this.nativeElement.classList.add(`slate-block-card`);
1527
- }
1528
- append() {
1529
- this.centerRootNodes.forEach(rootNode => !this.centerContainerElement.contains(rootNode) && this.centerContainerElement.appendChild(rootNode));
1530
- }
1531
- initializeCenter(rootNodes) {
1532
- this.centerRootNodes = rootNodes;
1533
- this.append();
1520
+ class FlavourRef {
1521
+ destroy() {
1522
+ this.instance.onDestroy();
1534
1523
  }
1535
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateBlockCard, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
1536
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: SlateBlockCard, isStandalone: true, selector: "slate-block-card, [slateBlockCard]", viewQueries: [{ propertyName: "centerContainer", first: true, predicate: ["centerContainer"], descendants: true, static: true }], ngImport: i0, template: "<span card-target=\"card-left\" class=\"card-left\">{{ '\\uFEFF' }}</span>\n<div card-target=\"card-center\" #centerContainer></div>\n<span card-target=\"card-right\" class=\"card-right\">{{ '\\uFEFF' }}</span>\n" }); }
1537
1524
  }
1538
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateBlockCard, decorators: [{
1539
- type: Component,
1540
- args: [{ selector: 'slate-block-card, [slateBlockCard]', standalone: true, template: "<span card-target=\"card-left\" class=\"card-left\">{{ '\\uFEFF' }}</span>\n<div card-target=\"card-center\" #centerContainer></div>\n<span card-target=\"card-right\" class=\"card-right\">{{ '\\uFEFF' }}</span>\n" }]
1541
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { centerContainer: [{
1542
- type: ViewChild,
1543
- args: ['centerContainer', { static: true }]
1544
- }] } });
1545
-
1546
- class FlavourRef {
1525
+ class BlockCardRef {
1547
1526
  destroy() {
1548
1527
  this.instance.onDestroy();
1549
1528
  }
@@ -1706,6 +1685,7 @@ class BaseFlavour {
1706
1685
  }
1707
1686
  }
1708
1687
 
1688
+ const DEFAULT_ELEMENT_HEIGHT = 24;
1709
1689
  class BaseElementFlavour extends BaseFlavour {
1710
1690
  constructor() {
1711
1691
  super(...arguments);
@@ -1800,6 +1780,9 @@ class BaseElementFlavour extends BaseFlavour {
1800
1780
  readonly: this._context.readonly
1801
1781
  };
1802
1782
  }
1783
+ getRealHeight() {
1784
+ return Promise.resolve(this.nativeElement.offsetHeight);
1785
+ }
1803
1786
  }
1804
1787
 
1805
1788
  class DefaultElementFlavour extends BaseElementFlavour {
@@ -1995,7 +1978,7 @@ const createEmptyOrVoidStringNode = () => {
1995
1978
  stringNode.setAttribute('data-slate-string', 'true');
1996
1979
  stringNode.setAttribute('data-slate-zero-width', 'z');
1997
1980
  stringNode.setAttribute('data-slate-length', '0');
1998
- const zeroWidthSpace = document.createTextNode('\uFEFF');
1981
+ const zeroWidthSpace = getZeroTextNode();
1999
1982
  stringNode.appendChild(zeroWidthSpace);
2000
1983
  stringNode.setAttribute('editable-text', '');
2001
1984
  return stringNode;
@@ -2006,7 +1989,7 @@ const createCompatibleStringNode = (text) => {
2006
1989
  stringNode.textContent = text;
2007
1990
  stringNode.setAttribute('editable-text', '');
2008
1991
  const zeroWidthSpan = document.createElement('span');
2009
- const zeroWidthSpace = document.createTextNode('\uFEFF');
1992
+ const zeroWidthSpace = getZeroTextNode();
2010
1993
  zeroWidthSpan.setAttribute('data-slate-zero-width', '');
2011
1994
  zeroWidthSpan.appendChild(zeroWidthSpace);
2012
1995
  stringNode.appendChild(zeroWidthSpan);
@@ -2016,7 +1999,7 @@ const createLineBreakEmptyStringDOM = (elementStringLength) => {
2016
1999
  const stringNode = document.createElement('span');
2017
2000
  stringNode.setAttribute('data-slate-zero-width', 'n');
2018
2001
  stringNode.setAttribute('data-slate-length', `${elementStringLength}`);
2019
- const zeroWidthSpace = document.createTextNode(`\uFEFF`);
2002
+ const zeroWidthSpace = getZeroTextNode();
2020
2003
  stringNode.appendChild(zeroWidthSpace);
2021
2004
  const brNode = document.createElement('br');
2022
2005
  stringNode.appendChild(brNode);
@@ -2226,6 +2209,49 @@ const createText = (text) => {
2226
2209
  return { nativeElement };
2227
2210
  };
2228
2211
 
2212
+ const SLATE_BLOCK_CARD_CLASS_NAME = 'slate-block-card';
2213
+ class SlateBlockCard {
2214
+ onInit() {
2215
+ const nativeElement = document.createElement('div');
2216
+ nativeElement.classList.add(SLATE_BLOCK_CARD_CLASS_NAME);
2217
+ this.nativeElement = nativeElement;
2218
+ this.createContent();
2219
+ }
2220
+ createContent() {
2221
+ const leftCaret = document.createElement('span');
2222
+ leftCaret.setAttribute(`card-target`, 'card-left');
2223
+ leftCaret.classList.add('card-left');
2224
+ leftCaret.appendChild(getZeroTextNode());
2225
+ const rightCaret = document.createElement('span');
2226
+ rightCaret.setAttribute(`card-target`, 'card-right');
2227
+ rightCaret.classList.add('card-right');
2228
+ rightCaret.appendChild(getZeroTextNode());
2229
+ const center = document.createElement('div');
2230
+ center.setAttribute(`card-target`, 'card-center');
2231
+ this.nativeElement.appendChild(leftCaret);
2232
+ this.nativeElement.appendChild(center);
2233
+ this.nativeElement.appendChild(rightCaret);
2234
+ this.centerContainer = center;
2235
+ }
2236
+ append() {
2237
+ this.centerRootNodes.forEach(rootNode => !this.centerContainer.contains(rootNode) && this.centerContainer.appendChild(rootNode));
2238
+ }
2239
+ initializeCenter(rootNodes) {
2240
+ this.centerRootNodes = rootNodes;
2241
+ this.append();
2242
+ }
2243
+ onDestroy() {
2244
+ this.nativeElement.remove();
2245
+ }
2246
+ }
2247
+ const getBlockCardByNativeElement = (nativeElement) => {
2248
+ const blockCardElement = nativeElement?.parentElement?.parentElement;
2249
+ if (blockCardElement && blockCardElement.classList.contains(SLATE_BLOCK_CARD_CLASS_NAME)) {
2250
+ return blockCardElement;
2251
+ }
2252
+ return null;
2253
+ };
2254
+
2229
2255
  class ListRender {
2230
2256
  constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2231
2257
  this.viewContext = viewContext;
@@ -2242,14 +2268,16 @@ class ListRender {
2242
2268
  initialize(children, parent, childrenContext) {
2243
2269
  this.initialized = true;
2244
2270
  this.children = children;
2271
+ const isRoot = parent === this.viewContext.editor;
2272
+ const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2245
2273
  const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2246
- children.forEach((descendant, index) => {
2247
- NODE_TO_INDEX.set(descendant, index);
2274
+ children.forEach((descendant, _index) => {
2275
+ NODE_TO_INDEX.set(descendant, firstIndex + _index);
2248
2276
  NODE_TO_PARENT.set(descendant, parent);
2249
- const context = getContext(index, descendant, parentPath, childrenContext, this.viewContext);
2277
+ const context = getContext(firstIndex + _index, descendant, parentPath, childrenContext, this.viewContext);
2250
2278
  const viewType = getViewType(descendant, parent, this.viewContext);
2251
2279
  const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2252
- const blockCard = createBlockCard(descendant, view, this.viewContainerRef, this.viewContext);
2280
+ const blockCard = createBlockCard(descendant, view, this.viewContext);
2253
2281
  this.views.push(view);
2254
2282
  this.contexts.push(context);
2255
2283
  this.viewTypes.push(viewType);
@@ -2274,6 +2302,8 @@ class ListRender {
2274
2302
  const outletParent = this.getOutletParent();
2275
2303
  const diffResult = this.differ.diff(children);
2276
2304
  const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2305
+ const isRoot = parent === this.viewContext.editor;
2306
+ const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2277
2307
  if (diffResult) {
2278
2308
  let firstRootNode = getRootNodes(this.views[0], this.blockCards[0])[0];
2279
2309
  const newContexts = [];
@@ -2281,16 +2311,17 @@ class ListRender {
2281
2311
  const newViews = [];
2282
2312
  const newBlockCards = [];
2283
2313
  diffResult.forEachItem(record => {
2284
- NODE_TO_INDEX.set(record.item, record.currentIndex);
2314
+ const currentIndex = firstIndex + record.currentIndex;
2315
+ NODE_TO_INDEX.set(record.item, currentIndex);
2285
2316
  NODE_TO_PARENT.set(record.item, parent);
2286
- let context = getContext(record.currentIndex, record.item, parentPath, childrenContext, this.viewContext);
2317
+ let context = getContext(currentIndex, record.item, parentPath, childrenContext, this.viewContext);
2287
2318
  const viewType = getViewType(record.item, parent, this.viewContext);
2288
2319
  newViewTypes.push(viewType);
2289
2320
  let view;
2290
2321
  let blockCard;
2291
2322
  if (record.previousIndex === null) {
2292
2323
  view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2293
- blockCard = createBlockCard(record.item, view, this.viewContainerRef, this.viewContext);
2324
+ blockCard = createBlockCard(record.item, view, this.viewContext);
2294
2325
  newContexts.push(context);
2295
2326
  newViews.push(view);
2296
2327
  newBlockCards.push(blockCard);
@@ -2303,7 +2334,7 @@ class ListRender {
2303
2334
  const previousBlockCard = this.blockCards[record.previousIndex];
2304
2335
  if (previousViewType !== viewType) {
2305
2336
  view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2306
- blockCard = createBlockCard(record.item, view, this.viewContainerRef, this.viewContext);
2337
+ blockCard = createBlockCard(record.item, view, this.viewContext);
2307
2338
  const firstRootNode = getRootNodes(previousView, previousBlockCard)[0];
2308
2339
  const newRootNodes = getRootNodes(view, blockCard);
2309
2340
  firstRootNode.replaceWith(...newRootNodes);
@@ -2351,16 +2382,16 @@ class ListRender {
2351
2382
  }
2352
2383
  else {
2353
2384
  const newContexts = [];
2354
- this.children.forEach((child, index) => {
2355
- NODE_TO_INDEX.set(child, index);
2385
+ this.children.forEach((child, _index) => {
2386
+ NODE_TO_INDEX.set(child, firstIndex + _index);
2356
2387
  NODE_TO_PARENT.set(child, parent);
2357
- let context = getContext(index, child, parentPath, childrenContext, this.viewContext);
2358
- const previousContext = this.contexts[index];
2388
+ let context = getContext(firstIndex + _index, child, parentPath, childrenContext, this.viewContext);
2389
+ const previousContext = this.contexts[_index];
2359
2390
  if (memoizedContext(this.viewContext, child, previousContext, context)) {
2360
2391
  context = previousContext;
2361
2392
  }
2362
2393
  else {
2363
- updateContext(this.views[index], context, this.viewContext);
2394
+ updateContext(this.views[_index], context, this.viewContext);
2364
2395
  }
2365
2396
  newContexts.push(context);
2366
2397
  });
@@ -2461,16 +2492,15 @@ function getViewType(item, parent, viewContext) {
2461
2492
  return isVoid ? VoidTextFlavour : (viewContext.renderText && viewContext.renderText(item)) || DefaultTextFlavour;
2462
2493
  }
2463
2494
  }
2464
- function createBlockCard(item, view, viewContainerRef, viewContext) {
2495
+ function createBlockCard(item, view, viewContext) {
2465
2496
  const isBlockCard = viewContext.editor.isBlockCard(item);
2466
2497
  if (isBlockCard) {
2467
2498
  const rootNodes = getRootNodes(view);
2468
- const blockCardComponentRef = viewContainerRef.createComponent(SlateBlockCard, {
2469
- injector: viewContainerRef.injector
2470
- });
2471
- blockCardComponentRef.instance.initializeCenter(rootNodes);
2472
- blockCardComponentRef.changeDetectorRef.detectChanges();
2473
- return blockCardComponentRef;
2499
+ const blockCardRef = new BlockCardRef();
2500
+ blockCardRef.instance = new SlateBlockCard();
2501
+ blockCardRef.instance.onInit();
2502
+ blockCardRef.instance.initializeCenter(rootNodes);
2503
+ return blockCardRef;
2474
2504
  }
2475
2505
  else {
2476
2506
  return null;
@@ -2522,6 +2552,17 @@ function executeAfterViewInit(editor) {
2522
2552
  // not correctly clipboardData on beforeinput
2523
2553
  const forceOnDOMPaste = IS_SAFARI;
2524
2554
  class SlateEditable {
2555
+ set virtualScroll(config) {
2556
+ this.virtualConfig = config;
2557
+ this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
2558
+ this.refreshVirtualViewAnimId = requestAnimationFrame(() => {
2559
+ this.refreshVirtualView();
2560
+ if (this.listRender.initialized) {
2561
+ this.listRender.update(this.renderedChildren, this.editor, this.context);
2562
+ }
2563
+ this.scheduleMeasureVisibleHeights();
2564
+ });
2565
+ }
2525
2566
  get hasBeforeInputSupport() {
2526
2567
  return HAS_BEFORE_INPUT_SUPPORT;
2527
2568
  }
@@ -2544,6 +2585,8 @@ class SlateEditable {
2544
2585
  this.isStrictDecorate = true;
2545
2586
  this.trackBy = () => null;
2546
2587
  this.readonly = false;
2588
+ this.virtualTopPadding = 0;
2589
+ this.virtualBottomPadding = 0;
2547
2590
  //#endregion
2548
2591
  //#region DOM attr
2549
2592
  this.spellCheck = false;
@@ -2557,6 +2600,15 @@ class SlateEditable {
2557
2600
  this.getOutletParent = () => {
2558
2601
  return this.elementRef.nativeElement;
2559
2602
  };
2603
+ this.virtualConfig = {
2604
+ enabled: false,
2605
+ scrollTop: 0,
2606
+ viewportHeight: 0
2607
+ };
2608
+ this.renderedChildren = [];
2609
+ this.virtualVisibleIndexes = new Set();
2610
+ this.measuredHeights = new Map();
2611
+ this.measurePending = false;
2560
2612
  }
2561
2613
  ngOnInit() {
2562
2614
  this.editor.injector = this.injector;
@@ -2611,12 +2663,15 @@ class SlateEditable {
2611
2663
  if (value && value.length) {
2612
2664
  this.editor.children = value;
2613
2665
  this.initializeContext();
2666
+ this.refreshVirtualView();
2667
+ const childrenForRender = this.renderedChildren;
2614
2668
  if (!this.listRender.initialized) {
2615
- this.listRender.initialize(this.editor.children, this.editor, this.context);
2669
+ this.listRender.initialize(childrenForRender, this.editor, this.context);
2616
2670
  }
2617
2671
  else {
2618
- this.listRender.update(this.editor.children, this.editor, this.context);
2672
+ this.listRender.update(childrenForRender, this.editor, this.context);
2619
2673
  }
2674
+ this.scheduleMeasureVisibleHeights();
2620
2675
  this.cdr.markForCheck();
2621
2676
  }
2622
2677
  }
@@ -2737,7 +2792,9 @@ class SlateEditable {
2737
2792
  ngDoCheck() { }
2738
2793
  forceRender() {
2739
2794
  this.updateContext();
2740
- this.listRender.update(this.editor.children, this.editor, this.context);
2795
+ this.refreshVirtualView();
2796
+ this.listRender.update(this.renderedChildren, this.editor, this.context);
2797
+ this.scheduleMeasureVisibleHeights();
2741
2798
  // repair collaborative editing when Chinese input is interrupted by other users' cursors
2742
2799
  // when the DOMElement where the selection is located is removed
2743
2800
  // the compositionupdate and compositionend events will no longer be fired
@@ -2776,7 +2833,9 @@ class SlateEditable {
2776
2833
  render() {
2777
2834
  const changed = this.updateContext();
2778
2835
  if (changed) {
2779
- this.listRender.update(this.editor.children, this.editor, this.context);
2836
+ this.refreshVirtualView();
2837
+ this.listRender.update(this.renderedChildren, this.editor, this.context);
2838
+ this.scheduleMeasureVisibleHeights();
2780
2839
  }
2781
2840
  }
2782
2841
  updateContext() {
@@ -2839,6 +2898,128 @@ class SlateEditable {
2839
2898
  decorations.push(...placeholderDecorations);
2840
2899
  return decorations;
2841
2900
  }
2901
+ shouldUseVirtual() {
2902
+ return !!(this.virtualConfig && this.virtualConfig.enabled);
2903
+ }
2904
+ refreshVirtualView() {
2905
+ const children = (this.editor.children || []);
2906
+ if (!children.length || !this.shouldUseVirtual()) {
2907
+ this.renderedChildren = children;
2908
+ this.virtualTopPadding = 0;
2909
+ this.virtualBottomPadding = 0;
2910
+ this.virtualVisibleIndexes.clear();
2911
+ return;
2912
+ }
2913
+ const scrollTop = this.virtualConfig.scrollTop ?? 0;
2914
+ const viewportHeight = this.virtualConfig.viewportHeight ?? 0;
2915
+ if (!viewportHeight) {
2916
+ // 已经启用虚拟滚动,但可视区域高度还未获取到,先置空不渲染
2917
+ this.renderedChildren = [];
2918
+ this.virtualTopPadding = 0;
2919
+ this.virtualBottomPadding = 0;
2920
+ this.virtualVisibleIndexes.clear();
2921
+ return;
2922
+ }
2923
+ const bufferCount = this.virtualConfig.bufferCount ?? VIRTUAL_SCROLL_DEFAULT_BUFFER_COUNT;
2924
+ const heights = children.map((_, idx) => this.getBlockHeight(idx));
2925
+ const accumulatedHeights = this.buildAccumulatedHeight(heights);
2926
+ const total = accumulatedHeights[accumulatedHeights.length - 1] || 0;
2927
+ let visibleStart = 0;
2928
+ // 按真实或估算高度往后累加,找到滚动起点所在块
2929
+ while (visibleStart < heights.length && accumulatedHeights[visibleStart + 1] <= scrollTop) {
2930
+ visibleStart++;
2931
+ }
2932
+ // 向上预留 bufferCount 块
2933
+ const startIndex = Math.max(0, visibleStart - bufferCount);
2934
+ const top = accumulatedHeights[startIndex];
2935
+ const bufferBelowHeight = this.getBufferBelowHeight(viewportHeight, visibleStart, bufferCount);
2936
+ const targetHeight = accumulatedHeights[visibleStart] - top + viewportHeight + bufferBelowHeight;
2937
+ const visible = [];
2938
+ const visibleIndexes = [];
2939
+ let accumulated = 0;
2940
+ let cursor = startIndex;
2941
+ // 循环累计高度超出目标高度(可视高度 + 上下 buffer)
2942
+ while (cursor < children.length && accumulated < targetHeight) {
2943
+ visible.push(children[cursor]);
2944
+ visibleIndexes.push(cursor);
2945
+ accumulated += this.getBlockHeight(cursor);
2946
+ cursor++;
2947
+ }
2948
+ const bottom = Math.max(total - top - accumulated, 0); // 下占位高度
2949
+ this.renderedChildren = visible.length ? visible : children;
2950
+ // padding 占位
2951
+ this.virtualTopPadding = this.renderedChildren === visible ? Math.round(top) : 0;
2952
+ this.virtualBottomPadding = this.renderedChildren === visible ? Math.round(bottom) : 0;
2953
+ this.virtualVisibleIndexes = new Set(visibleIndexes);
2954
+ }
2955
+ getBlockHeight(index) {
2956
+ const node = this.editor.children[index];
2957
+ if (!node) {
2958
+ return VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT;
2959
+ }
2960
+ const key = AngularEditor.findKey(this.editor, node);
2961
+ return this.measuredHeights.get(key.id) ?? VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT;
2962
+ }
2963
+ buildAccumulatedHeight(heights) {
2964
+ const accumulatedHeights = new Array(heights.length + 1).fill(0);
2965
+ for (let i = 0; i < heights.length; i++) {
2966
+ // 存储前 i 个的累计高度
2967
+ accumulatedHeights[i + 1] = accumulatedHeights[i] + heights[i];
2968
+ }
2969
+ return accumulatedHeights;
2970
+ }
2971
+ getBufferBelowHeight(viewportHeight, visibleStart, bufferCount) {
2972
+ let blockHeight = 0;
2973
+ let start = visibleStart;
2974
+ // 循环累计高度超出视图高度代表找到向下缓冲区的起始位置
2975
+ while (blockHeight < viewportHeight) {
2976
+ blockHeight += this.getBlockHeight(start);
2977
+ start++;
2978
+ }
2979
+ let bufferHeight = 0;
2980
+ for (let i = start; i < start + bufferCount; i++) {
2981
+ bufferHeight += this.getBlockHeight(i);
2982
+ }
2983
+ return bufferHeight;
2984
+ }
2985
+ scheduleMeasureVisibleHeights() {
2986
+ if (!this.shouldUseVirtual()) {
2987
+ return;
2988
+ }
2989
+ if (this.measurePending) {
2990
+ return;
2991
+ }
2992
+ this.measurePending = true;
2993
+ this.measureVisibleHeightsAnimId && cancelAnimationFrame(this.measureVisibleHeightsAnimId);
2994
+ this.measureVisibleHeightsAnimId = requestAnimationFrame(() => {
2995
+ this.measureVisibleHeights();
2996
+ this.measurePending = false;
2997
+ });
2998
+ }
2999
+ measureVisibleHeights() {
3000
+ const children = (this.editor.children || []);
3001
+ this.virtualVisibleIndexes.forEach(index => {
3002
+ const node = children[index];
3003
+ if (!node) {
3004
+ return;
3005
+ }
3006
+ const key = AngularEditor.findKey(this.editor, node);
3007
+ // 跳过已测过的块
3008
+ if (this.measuredHeights.has(key.id)) {
3009
+ return;
3010
+ }
3011
+ const view = ELEMENT_TO_COMPONENT.get(node);
3012
+ if (!view) {
3013
+ return;
3014
+ }
3015
+ view.getRealHeight()?.then(height => {
3016
+ const actualHeight = height +
3017
+ parseFloat(getComputedStyle(view.nativeElement).marginTop) +
3018
+ parseFloat(getComputedStyle(view.nativeElement).marginBottom);
3019
+ this.measuredHeights.set(key.id, actualHeight);
3020
+ });
3021
+ });
3022
+ }
2842
3023
  //#region event proxy
2843
3024
  addEventListener(eventName, listener, target = this.elementRef.nativeElement) {
2844
3025
  this.manualListeners.push(this.renderer2.listen(target, eventName, (event) => {
@@ -3538,30 +3719,37 @@ class SlateEditable {
3538
3719
  EDITOR_TO_ON_CHANGE.delete(this.editor);
3539
3720
  }
3540
3721
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateEditable, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component }); }
3541
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: SlateEditable, isStandalone: true, selector: "slate-editable", inputs: { editor: "editor", renderElement: "renderElement", renderLeaf: "renderLeaf", renderText: "renderText", decorate: "decorate", placeholderDecorate: "placeholderDecorate", scrollSelectionIntoView: "scrollSelectionIntoView", isStrictDecorate: "isStrictDecorate", trackBy: "trackBy", readonly: "readonly", placeholder: "placeholder", beforeInput: "beforeInput", blur: "blur", click: "click", compositionEnd: "compositionEnd", compositionUpdate: "compositionUpdate", compositionStart: "compositionStart", copy: "copy", cut: "cut", dragOver: "dragOver", dragStart: "dragStart", dragEnd: "dragEnd", drop: "drop", focus: "focus", keydown: "keydown", paste: "paste", spellCheck: "spellCheck", autoCorrect: "autoCorrect", autoCapitalize: "autoCapitalize" }, host: { properties: { "attr.contenteditable": "readonly ? undefined : true", "attr.role": "readonly ? undefined : 'textbox'", "attr.spellCheck": "!hasBeforeInputSupport ? false : spellCheck", "attr.autoCorrect": "!hasBeforeInputSupport ? 'false' : autoCorrect", "attr.autoCapitalize": "!hasBeforeInputSupport ? 'false' : autoCapitalize", "attr.data-slate-editor": "this.dataSlateEditor", "attr.data-slate-node": "this.dataSlateNode", "attr.data-gramm": "this.dataGramm" }, classAttribute: "slate-editable-container" }, providers: [
3722
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: SlateEditable, isStandalone: true, selector: "slate-editable", inputs: { editor: "editor", renderElement: "renderElement", renderLeaf: "renderLeaf", renderText: "renderText", decorate: "decorate", placeholderDecorate: "placeholderDecorate", scrollSelectionIntoView: "scrollSelectionIntoView", isStrictDecorate: "isStrictDecorate", trackBy: "trackBy", readonly: "readonly", placeholder: "placeholder", virtualScroll: "virtualScroll", beforeInput: "beforeInput", blur: "blur", click: "click", compositionEnd: "compositionEnd", compositionUpdate: "compositionUpdate", compositionStart: "compositionStart", copy: "copy", cut: "cut", dragOver: "dragOver", dragStart: "dragStart", dragEnd: "dragEnd", drop: "drop", focus: "focus", keydown: "keydown", paste: "paste", spellCheck: "spellCheck", autoCorrect: "autoCorrect", autoCapitalize: "autoCapitalize" }, host: { properties: { "attr.contenteditable": "readonly ? undefined : true", "attr.role": "readonly ? undefined : 'textbox'", "attr.spellCheck": "!hasBeforeInputSupport ? false : spellCheck", "attr.autoCorrect": "!hasBeforeInputSupport ? 'false' : autoCorrect", "attr.autoCapitalize": "!hasBeforeInputSupport ? 'false' : autoCapitalize", "style.--virtual-top-padding.px": "this.virtualTopPadding", "style.--virtual-bottom-padding.px": "this.virtualBottomPadding", "attr.data-slate-editor": "this.dataSlateEditor", "attr.data-slate-node": "this.dataSlateNode", "attr.data-gramm": "this.dataGramm" }, classAttribute: "slate-editable-container" }, providers: [
3542
3723
  {
3543
3724
  provide: NG_VALUE_ACCESSOR,
3544
3725
  useExisting: forwardRef(() => SlateEditable),
3545
3726
  multi: true
3546
3727
  }
3547
- ], usesOnChanges: true, ngImport: i0, template: "", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3728
+ ], usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3548
3729
  }
3549
3730
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateEditable, decorators: [{
3550
3731
  type: Component,
3551
- args: [{ selector: 'slate-editable', host: {
3732
+ args: [{
3733
+ selector: 'slate-editable',
3734
+ host: {
3552
3735
  class: 'slate-editable-container',
3553
3736
  '[attr.contenteditable]': 'readonly ? undefined : true',
3554
3737
  '[attr.role]': `readonly ? undefined : 'textbox'`,
3555
3738
  '[attr.spellCheck]': `!hasBeforeInputSupport ? false : spellCheck`,
3556
3739
  '[attr.autoCorrect]': `!hasBeforeInputSupport ? 'false' : autoCorrect`,
3557
3740
  '[attr.autoCapitalize]': `!hasBeforeInputSupport ? 'false' : autoCapitalize`
3558
- }, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
3741
+ },
3742
+ template: '',
3743
+ changeDetection: ChangeDetectionStrategy.OnPush,
3744
+ providers: [
3559
3745
  {
3560
3746
  provide: NG_VALUE_ACCESSOR,
3561
3747
  useExisting: forwardRef(() => SlateEditable),
3562
3748
  multi: true
3563
3749
  }
3564
- ], imports: [], template: "" }]
3750
+ ],
3751
+ imports: []
3752
+ }]
3565
3753
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i0.Injector }], propDecorators: { editor: [{
3566
3754
  type: Input
3567
3755
  }], renderElement: [{
@@ -3584,6 +3772,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
3584
3772
  type: Input
3585
3773
  }], placeholder: [{
3586
3774
  type: Input
3775
+ }], virtualScroll: [{
3776
+ type: Input
3777
+ }], virtualTopPadding: [{
3778
+ type: HostBinding,
3779
+ args: ['style.--virtual-top-padding.px']
3780
+ }], virtualBottomPadding: [{
3781
+ type: HostBinding,
3782
+ args: ['style.--virtual-bottom-padding.px']
3587
3783
  }], beforeInput: [{
3588
3784
  type: Input
3589
3785
  }], blur: [{
@@ -3709,35 +3905,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
3709
3905
  }]
3710
3906
  }], ctorParameters: () => [{ type: i0.ElementRef }] });
3711
3907
 
3712
- class SlateString {
3713
- ngOnInit() { }
3714
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateString, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3715
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: SlateString, isStandalone: true, selector: "span[slateString]", inputs: { context: "context", viewContext: "viewContext" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3716
- }
3717
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateString, decorators: [{
3718
- type: Component,
3719
- args: [{
3720
- selector: 'span[slateString]',
3721
- template: '',
3722
- changeDetection: ChangeDetectionStrategy.OnPush,
3723
- standalone: true
3724
- }]
3725
- }], propDecorators: { context: [{
3726
- type: Input
3727
- }], viewContext: [{
3728
- type: Input
3729
- }] } });
3730
-
3731
3908
  class SlateModule {
3732
3909
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3733
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, imports: [CommonModule, SlateEditable, SlateBlockCard, SlateChildrenOutlet, SlateString], exports: [SlateEditable, SlateChildrenOutlet, SlateString] }); }
3910
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, imports: [CommonModule, SlateEditable, SlateChildrenOutlet], exports: [SlateEditable, SlateChildrenOutlet] }); }
3734
3911
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, imports: [CommonModule] }); }
3735
3912
  }
3736
3913
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, decorators: [{
3737
3914
  type: NgModule,
3738
3915
  args: [{
3739
- imports: [CommonModule, SlateEditable, SlateBlockCard, SlateChildrenOutlet, SlateString],
3740
- exports: [SlateEditable, SlateChildrenOutlet, SlateString],
3916
+ imports: [CommonModule, SlateEditable, SlateChildrenOutlet],
3917
+ exports: [SlateEditable, SlateChildrenOutlet],
3741
3918
  providers: []
3742
3919
  }]
3743
3920
  }] });
@@ -3880,6 +4057,9 @@ class BaseElementComponent extends BaseComponent {
3880
4057
  readonly: this._context.readonly
3881
4058
  };
3882
4059
  }
4060
+ getRealHeight() {
4061
+ return Promise.resolve(this.nativeElement.offsetHeight);
4062
+ }
3883
4063
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: BaseElementComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
3884
4064
  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 }); }
3885
4065
  }
@@ -4034,5 +4214,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
4034
4214
  * Generated bundle index. Do not edit.
4035
4215
  */
4036
4216
 
4037
- export { AngularEditor, BaseComponent, BaseElementComponent, BaseElementFlavour, BaseFlavour, BaseLeafComponent, BaseLeafFlavour, BaseTextComponent, BaseTextFlavour, 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, PLACEHOLDER_SYMBOL, SlateChildrenOutlet, SlateEditable, SlateErrorCode, SlateFragmentAttributeKey, SlateModule, SlateString, VoidTextFlavour, blobAsString, buildHTMLText, check, completeTable, createClipboardData, createText, createThrottleRAF, defaultScrollSelectionIntoView, fallbackCopyText, getCardTargetAttribute, getClipboardData, getClipboardFromHTMLText, getContentHeight, getDataTransferClipboard, getDataTransferClipboardText, getNavigatorClipboard, getPlainText, getSelection, getSlateFragmentAttribute, hasAfterContextChange, hasBeforeContextChange, hasBlockCard, hasBlockCardWithNode, hotkeys, isCardCenterByTargetAttr, isCardLeft, isCardLeftByTargetAttr, isCardRightByTargetAttr, isClipboardFile, isClipboardReadSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isComponentType, isDOMText, isDecoratorRangeListEqual, isFlavourType, isInvalidTable, isTemplateRef, isValid, normalize, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setNavigatorClipboard, shallowCompare, stripHtml, withAngular };
4217
+ 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, PLACEHOLDER_SYMBOL, SLATE_BLOCK_CARD_CLASS_NAME, SlateBlockCard, SlateChildrenOutlet, SlateEditable, SlateErrorCode, SlateFragmentAttributeKey, SlateModule, VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT, VIRTUAL_SCROLL_DEFAULT_BUFFER_COUNT, VoidTextFlavour, blobAsString, buildHTMLText, check, completeTable, createClipboardData, createText, createThrottleRAF, defaultScrollSelectionIntoView, fallbackCopyText, getBlockCardByNativeElement, getCardTargetAttribute, getClipboardData, getClipboardFromHTMLText, getContentHeight, getDataTransferClipboard, getDataTransferClipboardText, getNavigatorClipboard, getPlainText, getSelection, getSlateFragmentAttribute, getZeroTextNode, hasAfterContextChange, hasBeforeContextChange, hasBlockCard, hasBlockCardWithNode, hotkeys, isCardCenterByTargetAttr, isCardLeft, isCardLeftByTargetAttr, isCardRightByTargetAttr, isClipboardFile, isClipboardReadSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isComponentType, isDOMText, isDecoratorRangeListEqual, isFlavourType, isInvalidTable, isTemplateRef, isValid, normalize, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setNavigatorClipboard, shallowCompare, stripHtml, withAngular };
4038
4218
  //# sourceMappingURL=slate-angular.mjs.map