slate-angular 20.2.0-next.0 → 20.2.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/slate-angular.mjs +645 -141
- package/fesm2022/slate-angular.mjs.map +1 -1
- package/index.d.ts +38 -10
- package/package.json +1 -1
- package/styles/index.scss +0 -13
|
@@ -472,6 +472,7 @@ const HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY &&
|
|
|
472
472
|
typeof globalThis.InputEvent.prototype.getTargetRanges === 'function';
|
|
473
473
|
const VIRTUAL_SCROLL_DEFAULT_BUFFER_COUNT = 3;
|
|
474
474
|
const VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT = 40;
|
|
475
|
+
const SLATE_DEBUG_KEY = '__SLATE_DEBUG__';
|
|
475
476
|
|
|
476
477
|
/**
|
|
477
478
|
* Hotkey mappings for each platform.
|
|
@@ -1134,6 +1135,9 @@ const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
|
|
|
1134
1135
|
NODE_TO_KEY.set(node, key);
|
|
1135
1136
|
}
|
|
1136
1137
|
};
|
|
1138
|
+
e.selectAll = () => {
|
|
1139
|
+
Transforms.select(e, []);
|
|
1140
|
+
};
|
|
1137
1141
|
return e;
|
|
1138
1142
|
};
|
|
1139
1143
|
|
|
@@ -1685,6 +1689,49 @@ class BaseFlavour {
|
|
|
1685
1689
|
}
|
|
1686
1690
|
}
|
|
1687
1691
|
|
|
1692
|
+
const SLATE_BLOCK_CARD_CLASS_NAME = 'slate-block-card';
|
|
1693
|
+
class SlateBlockCard {
|
|
1694
|
+
onInit() {
|
|
1695
|
+
const nativeElement = document.createElement('div');
|
|
1696
|
+
nativeElement.classList.add(SLATE_BLOCK_CARD_CLASS_NAME);
|
|
1697
|
+
this.nativeElement = nativeElement;
|
|
1698
|
+
this.createContent();
|
|
1699
|
+
}
|
|
1700
|
+
createContent() {
|
|
1701
|
+
const leftCaret = document.createElement('span');
|
|
1702
|
+
leftCaret.setAttribute(`card-target`, 'card-left');
|
|
1703
|
+
leftCaret.classList.add('card-left');
|
|
1704
|
+
leftCaret.appendChild(getZeroTextNode());
|
|
1705
|
+
const rightCaret = document.createElement('span');
|
|
1706
|
+
rightCaret.setAttribute(`card-target`, 'card-right');
|
|
1707
|
+
rightCaret.classList.add('card-right');
|
|
1708
|
+
rightCaret.appendChild(getZeroTextNode());
|
|
1709
|
+
const center = document.createElement('div');
|
|
1710
|
+
center.setAttribute(`card-target`, 'card-center');
|
|
1711
|
+
this.nativeElement.appendChild(leftCaret);
|
|
1712
|
+
this.nativeElement.appendChild(center);
|
|
1713
|
+
this.nativeElement.appendChild(rightCaret);
|
|
1714
|
+
this.centerContainer = center;
|
|
1715
|
+
}
|
|
1716
|
+
append() {
|
|
1717
|
+
this.centerRootNodes.forEach(rootNode => !this.centerContainer.contains(rootNode) && this.centerContainer.appendChild(rootNode));
|
|
1718
|
+
}
|
|
1719
|
+
initializeCenter(rootNodes) {
|
|
1720
|
+
this.centerRootNodes = rootNodes;
|
|
1721
|
+
this.append();
|
|
1722
|
+
}
|
|
1723
|
+
onDestroy() {
|
|
1724
|
+
this.nativeElement.remove();
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
const getBlockCardByNativeElement = (nativeElement) => {
|
|
1728
|
+
const blockCardElement = nativeElement?.parentElement?.parentElement;
|
|
1729
|
+
if (blockCardElement && blockCardElement.classList.contains(SLATE_BLOCK_CARD_CLASS_NAME)) {
|
|
1730
|
+
return blockCardElement;
|
|
1731
|
+
}
|
|
1732
|
+
return null;
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1688
1735
|
const DEFAULT_ELEMENT_HEIGHT = 24;
|
|
1689
1736
|
class BaseElementFlavour extends BaseFlavour {
|
|
1690
1737
|
constructor() {
|
|
@@ -1695,6 +1742,7 @@ class BaseElementFlavour extends BaseFlavour {
|
|
|
1695
1742
|
this.getOutletElement = () => {
|
|
1696
1743
|
return this.nativeElement.querySelector('.children-outlet');
|
|
1697
1744
|
};
|
|
1745
|
+
this.stableHeight = null;
|
|
1698
1746
|
}
|
|
1699
1747
|
get element() {
|
|
1700
1748
|
return this._context && this._context.element;
|
|
@@ -1780,8 +1828,21 @@ class BaseElementFlavour extends BaseFlavour {
|
|
|
1780
1828
|
readonly: this._context.readonly
|
|
1781
1829
|
};
|
|
1782
1830
|
}
|
|
1831
|
+
isStableHeight() {
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1783
1834
|
getRealHeight() {
|
|
1784
|
-
|
|
1835
|
+
if (this.isStableHeight() && this.stableHeight !== null) {
|
|
1836
|
+
return this.stableHeight;
|
|
1837
|
+
}
|
|
1838
|
+
const blockCard = getBlockCardByNativeElement(this.nativeElement);
|
|
1839
|
+
const target = blockCard || this.nativeElement;
|
|
1840
|
+
const computedStyle = getComputedStyle(target);
|
|
1841
|
+
const height = Math.ceil(target.getBoundingClientRect().height) + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom);
|
|
1842
|
+
if (this.isStableHeight()) {
|
|
1843
|
+
this.stableHeight = height;
|
|
1844
|
+
}
|
|
1845
|
+
return height;
|
|
1785
1846
|
}
|
|
1786
1847
|
}
|
|
1787
1848
|
|
|
@@ -2209,49 +2270,6 @@ const createText = (text) => {
|
|
|
2209
2270
|
return { nativeElement };
|
|
2210
2271
|
};
|
|
2211
2272
|
|
|
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
|
-
|
|
2255
2273
|
class ListRender {
|
|
2256
2274
|
constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
|
|
2257
2275
|
this.viewContext = viewContext;
|
|
@@ -2549,19 +2567,244 @@ function executeAfterViewInit(editor) {
|
|
|
2549
2567
|
clearAfterViewInitQueue(editor);
|
|
2550
2568
|
}
|
|
2551
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
|
+
|
|
2799
|
+
const JUST_NOW_UPDATED_VIRTUAL_VIEW = new WeakMap();
|
|
2800
|
+
const ELEMENT_KEY_TO_HEIGHTS = new WeakMap();
|
|
2552
2801
|
// not correctly clipboardData on beforeinput
|
|
2553
2802
|
const forceOnDOMPaste = IS_SAFARI;
|
|
2803
|
+
const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
|
|
2554
2804
|
class SlateEditable {
|
|
2555
2805
|
set virtualScroll(config) {
|
|
2556
2806
|
this.virtualConfig = config;
|
|
2557
|
-
this.
|
|
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
|
-
});
|
|
2807
|
+
this.doVirtualScroll();
|
|
2565
2808
|
}
|
|
2566
2809
|
get hasBeforeInputSupport() {
|
|
2567
2810
|
return HAS_BEFORE_INPUT_SUPPORT;
|
|
@@ -2585,8 +2828,6 @@ class SlateEditable {
|
|
|
2585
2828
|
this.isStrictDecorate = true;
|
|
2586
2829
|
this.trackBy = () => null;
|
|
2587
2830
|
this.readonly = false;
|
|
2588
|
-
this.virtualTopPadding = 0;
|
|
2589
|
-
this.virtualBottomPadding = 0;
|
|
2590
2831
|
//#endregion
|
|
2591
2832
|
//#region DOM attr
|
|
2592
2833
|
this.spellCheck = false;
|
|
@@ -2600,6 +2841,14 @@ class SlateEditable {
|
|
|
2600
2841
|
this.getOutletParent = () => {
|
|
2601
2842
|
return this.elementRef.nativeElement;
|
|
2602
2843
|
};
|
|
2844
|
+
this.getOutletElement = () => {
|
|
2845
|
+
if (this.virtualScrollInitialized) {
|
|
2846
|
+
return this.virtualCenterOutlet;
|
|
2847
|
+
}
|
|
2848
|
+
else {
|
|
2849
|
+
return null;
|
|
2850
|
+
}
|
|
2851
|
+
};
|
|
2603
2852
|
this.virtualConfig = {
|
|
2604
2853
|
enabled: false,
|
|
2605
2854
|
scrollTop: 0,
|
|
@@ -2608,7 +2857,9 @@ class SlateEditable {
|
|
|
2608
2857
|
this.renderedChildren = [];
|
|
2609
2858
|
this.virtualVisibleIndexes = new Set();
|
|
2610
2859
|
this.measuredHeights = new Map();
|
|
2611
|
-
|
|
2860
|
+
// the height from scroll container top to editor top height element
|
|
2861
|
+
this.businessHeight = 0;
|
|
2862
|
+
this.virtualScrollInitialized = false;
|
|
2612
2863
|
}
|
|
2613
2864
|
ngOnInit() {
|
|
2614
2865
|
this.editor.injector = this.injector;
|
|
@@ -2619,6 +2870,7 @@ class SlateEditable {
|
|
|
2619
2870
|
NODE_TO_ELEMENT.set(this.editor, this.elementRef.nativeElement);
|
|
2620
2871
|
ELEMENT_TO_NODE.set(this.elementRef.nativeElement, this.editor);
|
|
2621
2872
|
IS_READ_ONLY.set(this.editor, this.readonly);
|
|
2873
|
+
ELEMENT_KEY_TO_HEIGHTS.set(this.editor, this.measuredHeights);
|
|
2622
2874
|
EDITOR_TO_ON_CHANGE.set(this.editor, () => {
|
|
2623
2875
|
this.ngZone.run(() => {
|
|
2624
2876
|
this.onChange();
|
|
@@ -2632,7 +2884,8 @@ class SlateEditable {
|
|
|
2632
2884
|
// add browser class
|
|
2633
2885
|
let browserClass = IS_FIREFOX ? 'firefox' : IS_SAFARI ? 'safari' : '';
|
|
2634
2886
|
browserClass && this.elementRef.nativeElement.classList.add(browserClass);
|
|
2635
|
-
this.
|
|
2887
|
+
this.initializeVirtualScrolling();
|
|
2888
|
+
this.listRender = new ListRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
|
|
2636
2889
|
}
|
|
2637
2890
|
ngOnChanges(simpleChanges) {
|
|
2638
2891
|
if (!this.initialized) {
|
|
@@ -2663,8 +2916,9 @@ class SlateEditable {
|
|
|
2663
2916
|
if (value && value.length) {
|
|
2664
2917
|
this.editor.children = value;
|
|
2665
2918
|
this.initializeContext();
|
|
2666
|
-
this.refreshVirtualView();
|
|
2667
|
-
|
|
2919
|
+
const virtualView = this.refreshVirtualView();
|
|
2920
|
+
this.applyVirtualView(virtualView);
|
|
2921
|
+
const childrenForRender = virtualView.renderedChildren;
|
|
2668
2922
|
if (!this.listRender.initialized) {
|
|
2669
2923
|
this.listRender.initialize(childrenForRender, this.editor, this.context);
|
|
2670
2924
|
}
|
|
@@ -2704,7 +2958,25 @@ class SlateEditable {
|
|
|
2704
2958
|
}
|
|
2705
2959
|
toNativeSelection() {
|
|
2706
2960
|
try {
|
|
2707
|
-
|
|
2961
|
+
let { selection } = this.editor;
|
|
2962
|
+
if (this.virtualConfig?.enabled && selection) {
|
|
2963
|
+
const indics = Array.from(this.virtualVisibleIndexes.values());
|
|
2964
|
+
if (indics.length > 0) {
|
|
2965
|
+
const currentVisibleRange = {
|
|
2966
|
+
anchor: Editor.start(this.editor, [indics[0]]),
|
|
2967
|
+
focus: Editor.end(this.editor, [indics[indics.length - 1]])
|
|
2968
|
+
};
|
|
2969
|
+
const [start, end] = Range.edges(selection);
|
|
2970
|
+
const forwardSelection = { anchor: start, focus: end };
|
|
2971
|
+
const intersectedSelection = Range.intersection(forwardSelection, currentVisibleRange);
|
|
2972
|
+
if (!intersectedSelection || !Range.equals(intersectedSelection, forwardSelection)) {
|
|
2973
|
+
selection = intersectedSelection;
|
|
2974
|
+
if (isDebug) {
|
|
2975
|
+
this.debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2708
2980
|
const root = AngularEditor.findDocumentOrShadowRoot(this.editor);
|
|
2709
2981
|
const { activeElement } = root;
|
|
2710
2982
|
const domSelection = root.getSelection();
|
|
@@ -2792,9 +3064,18 @@ class SlateEditable {
|
|
|
2792
3064
|
ngDoCheck() { }
|
|
2793
3065
|
forceRender() {
|
|
2794
3066
|
this.updateContext();
|
|
2795
|
-
this.
|
|
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
|
+
}
|
|
2796
3077
|
this.listRender.update(this.renderedChildren, this.editor, this.context);
|
|
2797
|
-
this.
|
|
3078
|
+
this.remeasureHeightByIndics(visibleIndexes);
|
|
2798
3079
|
// repair collaborative editing when Chinese input is interrupted by other users' cursors
|
|
2799
3080
|
// when the DOMElement where the selection is located is removed
|
|
2800
3081
|
// the compositionupdate and compositionend events will no longer be fired
|
|
@@ -2833,8 +3114,9 @@ class SlateEditable {
|
|
|
2833
3114
|
render() {
|
|
2834
3115
|
const changed = this.updateContext();
|
|
2835
3116
|
if (changed) {
|
|
2836
|
-
this.refreshVirtualView();
|
|
2837
|
-
this.
|
|
3117
|
+
const virtualView = this.refreshVirtualView();
|
|
3118
|
+
this.applyVirtualView(virtualView);
|
|
3119
|
+
this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
|
|
2838
3120
|
this.scheduleMeasureVisibleHeights();
|
|
2839
3121
|
}
|
|
2840
3122
|
}
|
|
@@ -2901,64 +3183,248 @@ class SlateEditable {
|
|
|
2901
3183
|
shouldUseVirtual() {
|
|
2902
3184
|
return !!(this.virtualConfig && this.virtualConfig.enabled);
|
|
2903
3185
|
}
|
|
3186
|
+
initializeVirtualScrolling() {
|
|
3187
|
+
if (this.virtualScrollInitialized) {
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
if (this.virtualConfig && this.virtualConfig.enabled) {
|
|
3191
|
+
this.virtualScrollInitialized = true;
|
|
3192
|
+
this.virtualTopHeightElement = document.createElement('div');
|
|
3193
|
+
this.virtualTopHeightElement.classList.add('virtual-top-height');
|
|
3194
|
+
this.virtualTopHeightElement.contentEditable = 'false';
|
|
3195
|
+
this.virtualBottomHeightElement = document.createElement('div');
|
|
3196
|
+
this.virtualBottomHeightElement.classList.add('virtual-bottom-height');
|
|
3197
|
+
this.virtualBottomHeightElement.contentEditable = 'false';
|
|
3198
|
+
this.virtualCenterOutlet = document.createElement('div');
|
|
3199
|
+
this.virtualCenterOutlet.classList.add('virtual-center-outlet');
|
|
3200
|
+
this.elementRef.nativeElement.appendChild(this.virtualTopHeightElement);
|
|
3201
|
+
this.elementRef.nativeElement.appendChild(this.virtualCenterOutlet);
|
|
3202
|
+
this.elementRef.nativeElement.appendChild(this.virtualBottomHeightElement);
|
|
3203
|
+
this.businessHeight = this.virtualTopHeightElement.getBoundingClientRect()?.top ?? 0;
|
|
3204
|
+
let editorResizeObserverRectWidth = this.elementRef.nativeElement.getBoundingClientRect()?.width ?? 0;
|
|
3205
|
+
this.editorResizeObserver = new ResizeObserver(entries => {
|
|
3206
|
+
if (entries.length > 0 && entries[0].contentRect.width !== editorResizeObserverRectWidth) {
|
|
3207
|
+
editorResizeObserverRectWidth = entries[0].contentRect.width;
|
|
3208
|
+
this.remeasureHeightByIndics(Array.from(this.virtualVisibleIndexes));
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
this.editorResizeObserver.observe(this.elementRef.nativeElement);
|
|
3212
|
+
if (isDebug) {
|
|
3213
|
+
const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
|
|
3214
|
+
this.debugOverlay = new VirtualScrollDebugOverlay(doc);
|
|
3215
|
+
this.debugOverlay.init();
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
changeVirtualHeight(topHeight, bottomHeight) {
|
|
3220
|
+
if (!this.virtualScrollInitialized) {
|
|
3221
|
+
return;
|
|
3222
|
+
}
|
|
3223
|
+
this.virtualTopHeightElement.style.height = `${topHeight}px`;
|
|
3224
|
+
this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
|
|
3225
|
+
}
|
|
3226
|
+
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);
|
|
3232
|
+
}
|
|
3233
|
+
doVirtualScroll() {
|
|
3234
|
+
this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
|
|
3235
|
+
this.refreshVirtualViewAnimId = requestAnimationFrame(() => {
|
|
3236
|
+
let virtualView = this.refreshVirtualView();
|
|
3237
|
+
let diff = this.diffVirtualView(virtualView);
|
|
3238
|
+
if (!diff.isDiff) {
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
if (diff.isMissingTop) {
|
|
3242
|
+
const result = this.remeasureHeightByIndics(diff.diffTopRenderedIndexes);
|
|
3243
|
+
if (result) {
|
|
3244
|
+
virtualView = this.refreshVirtualView();
|
|
3245
|
+
diff = this.diffVirtualView(virtualView, 'second');
|
|
3246
|
+
if (!diff.isDiff) {
|
|
3247
|
+
return;
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
this.applyVirtualView(virtualView);
|
|
3252
|
+
if (this.listRender.initialized) {
|
|
3253
|
+
this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
|
|
3254
|
+
if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
|
|
3255
|
+
this.toNativeSelection();
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
this.scheduleMeasureVisibleHeights();
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
2904
3261
|
refreshVirtualView() {
|
|
2905
3262
|
const children = (this.editor.children || []);
|
|
2906
3263
|
if (!children.length || !this.shouldUseVirtual()) {
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
3264
|
+
return {
|
|
3265
|
+
renderedChildren: children,
|
|
3266
|
+
visibleIndexes: new Set(),
|
|
3267
|
+
top: 0,
|
|
3268
|
+
bottom: 0,
|
|
3269
|
+
heights: []
|
|
3270
|
+
};
|
|
2912
3271
|
}
|
|
2913
|
-
const scrollTop = this.virtualConfig.scrollTop
|
|
3272
|
+
const scrollTop = this.virtualConfig.scrollTop;
|
|
2914
3273
|
const viewportHeight = this.virtualConfig.viewportHeight ?? 0;
|
|
2915
3274
|
if (!viewportHeight) {
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
3275
|
+
return {
|
|
3276
|
+
renderedChildren: [],
|
|
3277
|
+
visibleIndexes: new Set(),
|
|
3278
|
+
top: 0,
|
|
3279
|
+
bottom: 0,
|
|
3280
|
+
heights: []
|
|
3281
|
+
};
|
|
2922
3282
|
}
|
|
2923
|
-
const
|
|
3283
|
+
const elementLength = children.length;
|
|
3284
|
+
const adjustedScrollTop = Math.max(0, scrollTop - this.businessHeight);
|
|
2924
3285
|
const heights = children.map((_, idx) => this.getBlockHeight(idx));
|
|
2925
3286
|
const accumulatedHeights = this.buildAccumulatedHeight(heights);
|
|
2926
|
-
const
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
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;
|
|
3287
|
+
const totalHeight = accumulatedHeights[elementLength];
|
|
3288
|
+
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
3289
|
+
const limitedScrollTop = Math.min(adjustedScrollTop, maxScrollTop);
|
|
3290
|
+
const viewBottom = limitedScrollTop + viewportHeight + this.businessHeight;
|
|
3291
|
+
let accumulatedOffset = 0;
|
|
3292
|
+
let visibleStartIndex = -1;
|
|
2937
3293
|
const visible = [];
|
|
2938
3294
|
const visibleIndexes = [];
|
|
2939
|
-
let
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3295
|
+
for (let i = 0; i < elementLength && accumulatedOffset < viewBottom; i++) {
|
|
3296
|
+
const currentHeight = heights[i];
|
|
3297
|
+
const nextOffset = accumulatedOffset + currentHeight;
|
|
3298
|
+
// 可视区域有交集,加入渲染
|
|
3299
|
+
if (nextOffset > limitedScrollTop && accumulatedOffset < viewBottom) {
|
|
3300
|
+
if (visibleStartIndex === -1)
|
|
3301
|
+
visibleStartIndex = i; // 第一个相交起始位置
|
|
3302
|
+
visible.push(children[i]);
|
|
3303
|
+
visibleIndexes.push(i);
|
|
3304
|
+
}
|
|
3305
|
+
accumulatedOffset = nextOffset;
|
|
3306
|
+
}
|
|
3307
|
+
if (visibleStartIndex === -1 && elementLength) {
|
|
3308
|
+
visibleStartIndex = elementLength - 1;
|
|
3309
|
+
visible.push(children[visibleStartIndex]);
|
|
3310
|
+
visibleIndexes.push(visibleStartIndex);
|
|
3311
|
+
}
|
|
3312
|
+
const visibleEndIndex = visibleStartIndex === -1 ? elementLength - 1 : (visibleIndexes[visibleIndexes.length - 1] ?? visibleStartIndex);
|
|
3313
|
+
const top = visibleStartIndex === -1 ? 0 : accumulatedHeights[visibleStartIndex];
|
|
3314
|
+
const bottom = totalHeight - accumulatedHeights[visibleEndIndex + 1];
|
|
3315
|
+
return {
|
|
3316
|
+
renderedChildren: visible.length ? visible : children,
|
|
3317
|
+
visibleIndexes: new Set(visibleIndexes),
|
|
3318
|
+
top,
|
|
3319
|
+
bottom,
|
|
3320
|
+
heights
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
applyVirtualView(virtualView) {
|
|
3324
|
+
this.renderedChildren = virtualView.renderedChildren;
|
|
3325
|
+
this.changeVirtualHeight(virtualView.top, virtualView.bottom);
|
|
3326
|
+
this.virtualVisibleIndexes = virtualView.visibleIndexes;
|
|
3327
|
+
}
|
|
3328
|
+
diffVirtualView(virtualView, stage = 'first') {
|
|
3329
|
+
if (!this.renderedChildren.length) {
|
|
3330
|
+
return {
|
|
3331
|
+
isDiff: true,
|
|
3332
|
+
diffTopRenderedIndexes: [],
|
|
3333
|
+
diffBottomRenderedIndexes: []
|
|
3334
|
+
};
|
|
3335
|
+
}
|
|
3336
|
+
const oldVisibleIndexes = [...this.virtualVisibleIndexes];
|
|
3337
|
+
const newVisibleIndexes = [...virtualView.visibleIndexes];
|
|
3338
|
+
const firstNewIndex = newVisibleIndexes[0];
|
|
3339
|
+
const lastNewIndex = newVisibleIndexes[newVisibleIndexes.length - 1];
|
|
3340
|
+
const firstOldIndex = oldVisibleIndexes[0];
|
|
3341
|
+
const lastOldIndex = oldVisibleIndexes[oldVisibleIndexes.length - 1];
|
|
3342
|
+
if (firstNewIndex !== firstOldIndex || lastNewIndex !== lastOldIndex) {
|
|
3343
|
+
const diffTopRenderedIndexes = [];
|
|
3344
|
+
const diffBottomRenderedIndexes = [];
|
|
3345
|
+
const isMissingTop = firstNewIndex !== firstOldIndex && firstNewIndex > firstOldIndex;
|
|
3346
|
+
const isAddedTop = firstNewIndex !== firstOldIndex && firstNewIndex < firstOldIndex;
|
|
3347
|
+
const isMissingBottom = lastNewIndex !== lastOldIndex && lastOldIndex > lastNewIndex;
|
|
3348
|
+
const isAddedBottom = lastNewIndex !== lastOldIndex && lastOldIndex < lastNewIndex;
|
|
3349
|
+
if (isMissingTop || isAddedBottom) {
|
|
3350
|
+
// 向下
|
|
3351
|
+
for (let index = 0; index < oldVisibleIndexes.length; index++) {
|
|
3352
|
+
const element = oldVisibleIndexes[index];
|
|
3353
|
+
if (!newVisibleIndexes.includes(element)) {
|
|
3354
|
+
diffTopRenderedIndexes.push(element);
|
|
3355
|
+
}
|
|
3356
|
+
else {
|
|
3357
|
+
break;
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
for (let index = newVisibleIndexes.length - 1; index >= 0; index--) {
|
|
3361
|
+
const element = newVisibleIndexes[index];
|
|
3362
|
+
if (!oldVisibleIndexes.includes(element)) {
|
|
3363
|
+
diffBottomRenderedIndexes.push(element);
|
|
3364
|
+
}
|
|
3365
|
+
else {
|
|
3366
|
+
break;
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
else if (isAddedTop || isMissingBottom) {
|
|
3371
|
+
// 向上
|
|
3372
|
+
for (let index = 0; index < newVisibleIndexes.length; index++) {
|
|
3373
|
+
const element = newVisibleIndexes[index];
|
|
3374
|
+
if (!oldVisibleIndexes.includes(element)) {
|
|
3375
|
+
diffTopRenderedIndexes.push(element);
|
|
3376
|
+
}
|
|
3377
|
+
else {
|
|
3378
|
+
break;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
for (let index = oldVisibleIndexes.length - 1; index >= 0; index--) {
|
|
3382
|
+
const element = oldVisibleIndexes[index];
|
|
3383
|
+
if (!newVisibleIndexes.includes(element)) {
|
|
3384
|
+
diffBottomRenderedIndexes.push(element);
|
|
3385
|
+
}
|
|
3386
|
+
else {
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
if (isDebug) {
|
|
3392
|
+
this.debugLog('log', `====== diffVirtualView stage: ${stage} ======`);
|
|
3393
|
+
this.debugLog('log', 'oldVisibleIndexes:', oldVisibleIndexes);
|
|
3394
|
+
this.debugLog('log', 'newVisibleIndexes:', newVisibleIndexes);
|
|
3395
|
+
this.debugLog('log', 'diffTopRenderedIndexes:', isMissingTop ? '-' : isAddedTop ? '+' : '-', diffTopRenderedIndexes, diffTopRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
|
|
3396
|
+
this.debugLog('log', 'diffBottomRenderedIndexes:', isAddedBottom ? '+' : isMissingBottom ? '-' : '+', diffBottomRenderedIndexes, diffBottomRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
|
|
3397
|
+
const needTop = virtualView.heights.slice(0, newVisibleIndexes[0]).reduce((acc, height) => acc + height, 0);
|
|
3398
|
+
const needBottom = virtualView.heights
|
|
3399
|
+
.slice(newVisibleIndexes[newVisibleIndexes.length - 1] + 1)
|
|
3400
|
+
.reduce((acc, height) => acc + height, 0);
|
|
3401
|
+
this.debugLog('log', 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
|
|
3402
|
+
this.debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
|
|
3403
|
+
this.debugLog('warn', '=========== Dividing line ===========');
|
|
3404
|
+
}
|
|
3405
|
+
return {
|
|
3406
|
+
isDiff: true,
|
|
3407
|
+
isMissingTop,
|
|
3408
|
+
isAddedTop,
|
|
3409
|
+
isMissingBottom,
|
|
3410
|
+
isAddedBottom,
|
|
3411
|
+
diffTopRenderedIndexes,
|
|
3412
|
+
diffBottomRenderedIndexes
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
return {
|
|
3416
|
+
isDiff: false,
|
|
3417
|
+
diffTopRenderedIndexes: [],
|
|
3418
|
+
diffBottomRenderedIndexes: []
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
getBlockHeight(index, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) {
|
|
2956
3422
|
const node = this.editor.children[index];
|
|
2957
3423
|
if (!node) {
|
|
2958
|
-
return
|
|
3424
|
+
return defaultHeight;
|
|
2959
3425
|
}
|
|
2960
3426
|
const key = AngularEditor.findKey(this.editor, node);
|
|
2961
|
-
return this.measuredHeights.get(key.id) ??
|
|
3427
|
+
return this.measuredHeights.get(key.id) ?? defaultHeight;
|
|
2962
3428
|
}
|
|
2963
3429
|
buildAccumulatedHeight(heights) {
|
|
2964
3430
|
const accumulatedHeights = new Array(heights.length + 1).fill(0);
|
|
@@ -2968,32 +3434,13 @@ class SlateEditable {
|
|
|
2968
3434
|
}
|
|
2969
3435
|
return accumulatedHeights;
|
|
2970
3436
|
}
|
|
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
3437
|
scheduleMeasureVisibleHeights() {
|
|
2986
3438
|
if (!this.shouldUseVirtual()) {
|
|
2987
3439
|
return;
|
|
2988
3440
|
}
|
|
2989
|
-
if (this.measurePending) {
|
|
2990
|
-
return;
|
|
2991
|
-
}
|
|
2992
|
-
this.measurePending = true;
|
|
2993
3441
|
this.measureVisibleHeightsAnimId && cancelAnimationFrame(this.measureVisibleHeightsAnimId);
|
|
2994
3442
|
this.measureVisibleHeightsAnimId = requestAnimationFrame(() => {
|
|
2995
3443
|
this.measureVisibleHeights();
|
|
2996
|
-
this.measurePending = false;
|
|
2997
3444
|
});
|
|
2998
3445
|
}
|
|
2999
3446
|
measureVisibleHeights() {
|
|
@@ -3004,7 +3451,7 @@ class SlateEditable {
|
|
|
3004
3451
|
return;
|
|
3005
3452
|
}
|
|
3006
3453
|
const key = AngularEditor.findKey(this.editor, node);
|
|
3007
|
-
//
|
|
3454
|
+
// 跳过已测过的块,除非强制测量
|
|
3008
3455
|
if (this.measuredHeights.has(key.id)) {
|
|
3009
3456
|
return;
|
|
3010
3457
|
}
|
|
@@ -3012,13 +3459,54 @@ class SlateEditable {
|
|
|
3012
3459
|
if (!view) {
|
|
3013
3460
|
return;
|
|
3014
3461
|
}
|
|
3015
|
-
view.getRealHeight()
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
}
|
|
3462
|
+
const ret = view.getRealHeight();
|
|
3463
|
+
if (ret instanceof Promise) {
|
|
3464
|
+
ret.then(height => {
|
|
3465
|
+
this.measuredHeights.set(key.id, height);
|
|
3466
|
+
});
|
|
3467
|
+
}
|
|
3468
|
+
else {
|
|
3469
|
+
this.measuredHeights.set(key.id, ret);
|
|
3470
|
+
}
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
remeasureHeightByIndics(indics) {
|
|
3474
|
+
const children = (this.editor.children || []);
|
|
3475
|
+
let isHeightChanged = false;
|
|
3476
|
+
indics.forEach(index => {
|
|
3477
|
+
const node = children[index];
|
|
3478
|
+
if (!node) {
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
const key = AngularEditor.findKey(this.editor, node);
|
|
3482
|
+
const view = ELEMENT_TO_COMPONENT.get(node);
|
|
3483
|
+
if (!view) {
|
|
3484
|
+
return;
|
|
3485
|
+
}
|
|
3486
|
+
const prevHeight = this.measuredHeights.get(key.id);
|
|
3487
|
+
const ret = view.getRealHeight();
|
|
3488
|
+
if (ret instanceof Promise) {
|
|
3489
|
+
ret.then(height => {
|
|
3490
|
+
if (height !== prevHeight) {
|
|
3491
|
+
this.measuredHeights.set(key.id, height);
|
|
3492
|
+
isHeightChanged = true;
|
|
3493
|
+
if (isDebug) {
|
|
3494
|
+
this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${height}`);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
3499
|
+
else {
|
|
3500
|
+
if (ret !== prevHeight) {
|
|
3501
|
+
this.measuredHeights.set(key.id, ret);
|
|
3502
|
+
isHeightChanged = true;
|
|
3503
|
+
if (isDebug) {
|
|
3504
|
+
this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${ret}`);
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3021
3508
|
});
|
|
3509
|
+
return isHeightChanged;
|
|
3022
3510
|
}
|
|
3023
3511
|
//#region event proxy
|
|
3024
3512
|
addEventListener(eventName, listener, target = this.elementRef.nativeElement) {
|
|
@@ -3544,6 +4032,11 @@ class SlateEditable {
|
|
|
3544
4032
|
Transforms.move(editor, { unit: 'word', reverse: isRTL });
|
|
3545
4033
|
return;
|
|
3546
4034
|
}
|
|
4035
|
+
if (isKeyHotkey('mod+a', event)) {
|
|
4036
|
+
this.editor.selectAll();
|
|
4037
|
+
event.preventDefault();
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
3547
4040
|
// COMPAT: Certain browsers don't support the `beforeinput` event, so we
|
|
3548
4041
|
// fall back to guessing at the input intention for hotkeys.
|
|
3549
4042
|
// COMPAT: In iOS, some of these hotkeys are handled in the
|
|
@@ -3711,6 +4204,9 @@ class SlateEditable {
|
|
|
3711
4204
|
}
|
|
3712
4205
|
//#endregion
|
|
3713
4206
|
ngOnDestroy() {
|
|
4207
|
+
this.editorResizeObserver?.disconnect();
|
|
4208
|
+
this.debugOverlay?.dispose();
|
|
4209
|
+
this.debugOverlay = undefined;
|
|
3714
4210
|
NODE_TO_ELEMENT.delete(this.editor);
|
|
3715
4211
|
this.manualListeners.forEach(manualListener => {
|
|
3716
4212
|
manualListener();
|
|
@@ -3719,7 +4215,7 @@ class SlateEditable {
|
|
|
3719
4215
|
EDITOR_TO_ON_CHANGE.delete(this.editor);
|
|
3720
4216
|
}
|
|
3721
4217
|
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 }); }
|
|
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", "
|
|
4218
|
+
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", "attr.data-slate-editor": "this.dataSlateEditor", "attr.data-slate-node": "this.dataSlateNode", "attr.data-gramm": "this.dataGramm" }, classAttribute: "slate-editable-container" }, providers: [
|
|
3723
4219
|
{
|
|
3724
4220
|
provide: NG_VALUE_ACCESSOR,
|
|
3725
4221
|
useExisting: forwardRef(() => SlateEditable),
|
|
@@ -3774,12 +4270,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
3774
4270
|
type: Input
|
|
3775
4271
|
}], virtualScroll: [{
|
|
3776
4272
|
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']
|
|
3783
4273
|
}], beforeInput: [{
|
|
3784
4274
|
type: Input
|
|
3785
4275
|
}], blur: [{
|
|
@@ -3976,6 +4466,7 @@ class BaseElementComponent extends BaseComponent {
|
|
|
3976
4466
|
}
|
|
3977
4467
|
return null;
|
|
3978
4468
|
};
|
|
4469
|
+
this.stableHeight = null;
|
|
3979
4470
|
}
|
|
3980
4471
|
get element() {
|
|
3981
4472
|
return this._context && this._context.element;
|
|
@@ -4057,8 +4548,21 @@ class BaseElementComponent extends BaseComponent {
|
|
|
4057
4548
|
readonly: this._context.readonly
|
|
4058
4549
|
};
|
|
4059
4550
|
}
|
|
4551
|
+
isStableHeight() {
|
|
4552
|
+
return false;
|
|
4553
|
+
}
|
|
4060
4554
|
getRealHeight() {
|
|
4061
|
-
|
|
4555
|
+
if (this.isStableHeight() && this.stableHeight !== null) {
|
|
4556
|
+
return this.stableHeight;
|
|
4557
|
+
}
|
|
4558
|
+
const blockCard = getBlockCardByNativeElement(this.nativeElement);
|
|
4559
|
+
const target = blockCard || this.nativeElement;
|
|
4560
|
+
const computedStyle = getComputedStyle(target);
|
|
4561
|
+
const height = Math.ceil(target.getBoundingClientRect().height) + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom);
|
|
4562
|
+
if (this.isStableHeight()) {
|
|
4563
|
+
this.stableHeight = height;
|
|
4564
|
+
}
|
|
4565
|
+
return height;
|
|
4062
4566
|
}
|
|
4063
4567
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: BaseElementComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4064
4568
|
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 }); }
|
|
@@ -4214,5 +4718,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
4214
4718
|
* Generated bundle index. Do not edit.
|
|
4215
4719
|
*/
|
|
4216
4720
|
|
|
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 };
|
|
4721
|
+
export { AngularEditor, BaseComponent, BaseElementComponent, BaseElementFlavour, BaseFlavour, BaseLeafComponent, BaseLeafFlavour, BaseTextComponent, BaseTextFlavour, BlockCardRef, DEFAULT_ELEMENT_HEIGHT, DefaultTextFlavour, EDITOR_TO_AFTER_VIEW_INIT_QUEUE, ELEMENT_KEY_TO_HEIGHTS, ELEMENT_TO_COMPONENT, FAKE_LEFT_BLOCK_CARD_OFFSET, FAKE_RIGHT_BLOCK_CARD_OFFSET, FlavourRef, HAS_BEFORE_INPUT_SUPPORT, IS_ANDROID, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_FIREFOX_LEGACY, IS_IOS, IS_QQBROWSER, IS_SAFARI, IS_UC_MOBILE, IS_WECHATBROWSER, JUST_NOW_UPDATED_VIRTUAL_VIEW, PLACEHOLDER_SYMBOL, SLATE_BLOCK_CARD_CLASS_NAME, SLATE_DEBUG_KEY, 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 };
|
|
4218
4722
|
//# sourceMappingURL=slate-angular.mjs.map
|