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

worktile / slate-angular / fb30d33e-e9ae-4e7b-a748-4e40a17cb98d

07 Jan 2026 03:35AM UTC coverage: 36.662% (+0.02%) from 36.647%
fb30d33e-e9ae-4e7b-a748-4e40a17cb98d

push

circleci

web-flow
feat(virtual-scroll): #WIK-19695 collapsed content will not be rendered (#330)

* feat(virtual-scroll): #WIK-19695 collapsed content will not be rendered

* fix: optimize

* chore: add changeset

394 of 1274 branches covered (30.93%)

Branch coverage included in aggregate %.

14 of 56 new or added lines in 3 files covered. (25.0%)

1 existing line in 1 file now uncovered.

1093 of 2782 relevant lines covered (39.29%)

24.09 hits per line

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

13.98
/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, VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT } 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 debugLog = (type: 'log' | 'warn', ...args: any[]) => {
1✔
19
    const doc = document;
×
20
    VirtualScrollDebugOverlay.log(doc, type, ...args);
×
21
};
22

23
export const measureHeightByElement = (editor: AngularEditor, element: Element) => {
1✔
24
    const key = AngularEditor.findKey(editor, element);
×
25
    const view = ELEMENT_TO_COMPONENT.get(element);
×
26
    if (!view) {
×
27
        return;
×
28
    }
29
    const ret = (view as BaseElementComponent | BaseElementFlavour).getRealHeight();
×
30
    const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
×
31
    heights.set(key.id, ret as number);
×
32
    return ret as number;
×
33
};
34

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

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

67
export const getRealHeightByElement = (
1✔
68
    editor: AngularEditor,
69
    element: Element,
70
    defaultHeight: number = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT,
×
71
    isVisible?: boolean
72
) => {
NEW
73
    const visible = isVisible ?? editor.isVisible(element);
×
NEW
74
    if (!visible) {
×
UNCOV
75
        return 0;
×
76
    }
77
    const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
×
78
    const key = AngularEditor.findKey(editor, element);
×
79
    const height = heights?.get(key.id);
×
80
    if (typeof height === 'number') {
×
81
        return height;
×
82
    }
83
    if (heights?.has(key.id)) {
×
84
        console.error('getBlockHeight: invalid height value', key.id, height);
×
85
    }
86
    return defaultHeight;
×
87
};
88

89
export const buildHeightsAndAccumulatedHeights = (editor: AngularEditor) => {
1✔
90
    const children = (editor.children || []) as Element[];
×
91
    const heights = new Array(children.length);
×
NEW
92
    const visibles = new Array(children.length);
×
93
    const accumulatedHeights = new Array(children.length + 1);
×
94
    accumulatedHeights[0] = 0;
×
95
    for (let i = 0; i < children.length; i++) {
×
NEW
96
        const isVisible = editor.isVisible(children[i]);
×
NEW
97
        visibles[i] = isVisible;
×
NEW
98
        const height = getRealHeightByElement(editor, children[i], VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT, isVisible);
×
99
        heights[i] = height;
×
100
        accumulatedHeights[i + 1] = accumulatedHeights[i] + height;
×
101
    }
NEW
102
    return { heights, accumulatedHeights, visibles };
×
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
};
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