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

worktile / slate-angular / 553851f4-f786-4b95-a339-b5c1f273ad6e

14 Jan 2026 04:37AM UTC coverage: 36.786% (-0.04%) from 36.822%
553851f4-f786-4b95-a339-b5c1f273ad6e

push

circleci

pubuzhixing8
feat(virtual-scroll): verify height type in cacheHeightByElement

402 of 1291 branches covered (31.14%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 1 file covered. (0.0%)

1104 of 2803 relevant lines covered (39.39%)

23.83 hits per line

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

14.71
/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✔
26
    if (!AngularEditor.isEnabledVirtualScroll(editor)) {
×
27
        return;
×
28
    }
NEW
29
    if (typeof height !== 'number') {
×
NEW
30
        console.error('cacheHeightByElement: height must be number', height);
×
NEW
31
        return;
×
32
    }
33
    const key = AngularEditor.findKey(editor, element);
×
34
    const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
×
35
    heights.set(key.id, height);
×
36
};
37

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

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

68
export const getBusinessTop = (editor: AngularEditor) => {
1✔
69
    return EDITOR_TO_BUSINESS_TOP.get(editor) ?? 0;
×
70
};
71

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

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

105
export const calculateVirtualTopHeight = (editor: AngularEditor, startIndex: number) => {
1✔
106
    const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
×
107
    return accumulatedHeights[startIndex] ?? 0;
×
108
};
109

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

120
    const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
×
121
    scrollTo((accumulatedHeights[anchorIndex] ?? 0) + getBusinessTop(editor));
×
122
    EDITOR_TO_IS_FROM_SCROLL_TO.set(editor, true);
×
123
    setTimeout(() => {
×
124
        console.log('scrollToElement: end scroll');
×
125
        EDITOR_TO_IS_FROM_SCROLL_TO.set(editor, false);
×
126
    }, 0);
127
};
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