• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

worktile / slate-angular / ed912481-ba2a-4021-b9f8-01d1ec3fc034

14 Jan 2026 04:27AM UTC coverage: 36.822% (-0.002%) from 36.824%
ed912481-ba2a-4021-b9f8-01d1ec3fc034

push

circleci

pubuzhixing8
feat(virtual-scroll): extract cacheHeightByElement

402 of 1290 branches covered (31.16%)

Branch coverage included in aggregate %.

2 of 9 new or added lines in 1 file covered. (22.22%)

1104 of 2800 relevant lines covered (39.43%)

23.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

15.31
/packages/src/utils/virtual-scroll.ts
1
import { Element } from 'slate';
2
import { AngularEditor } from '../plugins/angular-editor';
3
import { SLATE_DEBUG_KEY, SLATE_DEBUG_KEY_SCROLL_TOP } from './environment';
4
import { ELEMENT_TO_COMPONENT } from './weak-maps';
5
import { BaseElementComponent } from '../view/base';
6
import { BaseElementFlavour } from '../view/flavour/element';
7
import { VirtualScrollDebugOverlay } from '../components/editable/debug';
8

9
export const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
1✔
10
export const isDebugScrollTop = localStorage.getItem(SLATE_DEBUG_KEY_SCROLL_TOP) === 'true';
1✔
11

12
export const ELEMENT_KEY_TO_HEIGHTS = new WeakMap<AngularEditor, Map<string, number>>();
1✔
13

14
export const EDITOR_TO_BUSINESS_TOP = new WeakMap<AngularEditor, number>();
1✔
15

16
export const EDITOR_TO_ROOT_NODE_WIDTH = new WeakMap<AngularEditor, number>();
1✔
17

18
export const EDITOR_TO_IS_FROM_SCROLL_TO = new WeakMap<AngularEditor, boolean>();
1✔
19

20
export const debugLog = (type: 'log' | 'warn', ...args: any[]) => {
1✔
21
    const doc = document;
×
22
    VirtualScrollDebugOverlay.log(doc, type, ...args);
×
23
};
24

25
export const cacheHeightByElement = (editor: AngularEditor, element: Element, height: number) => {
1✔
NEW
26
    if (!AngularEditor.isEnabledVirtualScroll(editor)) {
×
NEW
27
        return;
×
28
    }
29
    const key = AngularEditor.findKey(editor, element);
×
NEW
30
    const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
×
NEW
31
    heights.set(key.id, height);
×
32
};
33

34
export const calcHeightByElement = (editor: AngularEditor, element: Element) => {
1✔
35
    const view = ELEMENT_TO_COMPONENT.get(element);
×
36
    if (!view) {
×
37
        return;
×
38
    }
NEW
39
    const height = (view as BaseElementComponent | BaseElementFlavour).calcHeight();
×
NEW
40
    cacheHeightByElement(editor, element, height);
×
NEW
41
    return height;
×
42
};
43

44
export const measureHeightByIndics = (editor: AngularEditor, indics: number[], force = false) => {
1!
45
    let hasChanged = false;
×
46
    indics.forEach((index, i) => {
×
47
        const element = editor.children[index] as Element;
×
48
        const preHeight = getCachedHeightByElement(editor, element);
×
49
        if (preHeight && !force) {
×
50
            if (isDebug) {
×
51
                const height = calcHeightByElement(editor, element);
×
52
                if (height !== preHeight) {
×
53
                    debugLog('warn', 'calcHeightByElement: height not equal, index: ', index, 'preHeight: ', preHeight, 'height: ', height);
×
54
                }
55
            }
56
            return;
×
57
        }
58
        hasChanged = true;
×
59
        calcHeightByElement(editor, element);
×
60
    });
61
    return hasChanged;
×
62
};
63

64
export const getBusinessTop = (editor: AngularEditor) => {
1✔
65
    return EDITOR_TO_BUSINESS_TOP.get(editor) ?? 0;
×
66
};
67

68
export const getCachedHeightByElement = (editor: AngularEditor, element: Element) => {
1✔
69
    const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
×
70
    const key = AngularEditor.findKey(editor, element);
×
71
    const height = heights?.get(key.id);
×
72
    if (typeof height === 'number') {
×
73
        return height;
×
74
    }
75
    if (heights?.has(key.id)) {
×
76
        console.error('getBlockHeight: invalid height value', key.id, height);
×
77
    }
78
    return null;
×
79
};
80

81
export const buildHeightsAndAccumulatedHeights = (editor: AngularEditor) => {
1✔
82
    const children = (editor.children || []) as Element[];
×
83
    const heights = new Array(children.length);
×
84
    const accumulatedHeights = new Array(children.length + 1);
×
85
    accumulatedHeights[0] = 0;
×
86
    for (let i = 0; i < children.length; i++) {
×
87
        let height = getCachedHeightByElement(editor, children[i]);
×
88
        if (height === null) {
×
89
            try {
×
90
                height = editor.getRoughHeight(children[i]);
×
91
            } catch (error) {
92
                console.error('buildHeightsAndAccumulatedHeights: getRoughHeight error', error);
×
93
            }
94
        }
95
        heights[i] = height;
×
96
        accumulatedHeights[i + 1] = accumulatedHeights[i] + height;
×
97
    }
98
    return { heights, accumulatedHeights };
×
99
};
100

101
export const calculateVirtualTopHeight = (editor: AngularEditor, startIndex: number) => {
1✔
102
    const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
×
103
    return accumulatedHeights[startIndex] ?? 0;
×
104
};
105

106
export const scrollToElement = (editor: AngularEditor, element: Element, scrollTo: (scrollTop: number) => void) => {
1✔
107
    const children = editor.children;
×
108
    if (!children.length) {
×
109
        return;
×
110
    }
111
    const anchorIndex = children.findIndex(item => item === element);
×
112
    if (anchorIndex < 0) {
×
113
        return;
×
114
    }
115

116
    const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
×
117
    scrollTo((accumulatedHeights[anchorIndex] ?? 0) + getBusinessTop(editor));
×
118
    EDITOR_TO_IS_FROM_SCROLL_TO.set(editor, true);
×
119
    setTimeout(() => {
×
120
        console.log('scrollToElement: end scroll');
×
121
        EDITOR_TO_IS_FROM_SCROLL_TO.set(editor, false);
×
122
    }, 0);
123
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc