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

atinc / ngx-tethys / 4209e40d-4a53-48f5-b396-81cec1d9cc0f

15 Apr 2025 10:18AM UTC coverage: 90.175% (+0.001%) from 90.174%
4209e40d-4a53-48f5-b396-81cec1d9cc0f

push

circleci

minlovehua
feat: demo

5591 of 6865 branches covered (81.44%)

Branch coverage included in aggregate %.

76 of 80 new or added lines in 36 files covered. (95.0%)

67 existing lines in 12 files now uncovered.

13353 of 14143 relevant lines covered (94.41%)

991.7 hits per line

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

83.33
/src/tree/tree.service.ts
1
import { coerceArray, isFunction } from 'ngx-tethys/util';
2
import { BehaviorSubject, Subject } from 'rxjs';
3
import { Injectable, OnDestroy } from '@angular/core';
4
import { ThyTreeNodeCheckState, ThyTreeNodeData, ThyTreeNode, IThyTreeService, ThyTreeFormatEmitEvent } from './tree.class';
5

6
function checkStateResolve(node: ThyTreeNode) {
7
    const checkedNodes = node.children.filter(n => n.isChecked === ThyTreeNodeCheckState.checked);
6,947✔
8
    const unCheckedNodes = node.children.filter(n => n.isChecked === ThyTreeNodeCheckState.unchecked);
6,947✔
9
    if (checkedNodes.length === node.children.length) {
925!
10
        return ThyTreeNodeCheckState.checked;
×
11
    } else if (unCheckedNodes.length === node.children.length) {
12
        return ThyTreeNodeCheckState.unchecked;
925✔
13
    } else {
788✔
14
        return ThyTreeNodeCheckState.indeterminate;
15
    }
16
}
137✔
17

18
type FlattenAllNodesCb = (treeNode: ThyTreeNode) => boolean;
19

20
/**
21
 * @internal
22
 */
1✔
23
@Injectable()
24
export class ThyTreeService implements IThyTreeService, OnDestroy {
92✔
25
    selectedNode!: ThyTreeNode;
92✔
26

92✔
27
    flattenNodes$ = new BehaviorSubject<ThyTreeNode[]>([]);
92✔
28

92✔
29
    flattenTreeNodes: ThyTreeNode[] = [];
92✔
30

92✔
31
    private originTreeNodes: ThyTreeNodeData[] = [];
1✔
32

1✔
33
    public treeNodes: ThyTreeNode[] = [];
34

35
    public checkStateResolve: (node: ThyTreeNode) => ThyTreeNodeCheckState = checkStateResolve;
36

58✔
37
    statusChange$ = new Subject<ThyTreeFormatEmitEvent>();
160✔
38

39
    constructor() {
40
        this.statusChange$.pipe().subscribe(event => {
1,292✔
41
            this.syncFlattenTreeNodes();
1,292✔
42
            this.syncNodeCheckState(event.node);
1,292✔
43
        });
44
    }
235!
45

1,527✔
46
    public initializeTreeNodes(rootNodes: ThyTreeNodeData[]) {
47
        this.originTreeNodes = rootNodes || [];
1,258,829✔
48
        this.treeNodes = (rootNodes || []).map(node => new ThyTreeNode(node, null, this));
1,258,845✔
49
    }
1,258,845!
50

1,258,845✔
51
    public syncFlattenTreeNodes(): ThyTreeNode[] {
1,257,302✔
52
        this.flattenTreeNodes = this.getParallelTreeNodes(this.treeNodes, false);
53
        this.flattenNodes$.next(this.flattenTreeNodes);
54
        return this.flattenTreeNodes;
55
    }
1,527✔
56

1,527✔
57
    private getParallelTreeNodes(rootTrees: ThyTreeNode[] = this.treeNodes, flattenAllNodes: boolean | FlattenAllNodesCb = true) {
58
        const flattenTreeData: ThyTreeNode[] = [];
×
59
        function _getParallelTreeNodes(list: ThyTreeNode[]) {
2✔
60
            return list.forEach((treeNode, index) => {
61
                flattenTreeData.push(treeNode);
UNCOV
62
                const flattenAllNodesFlag = isFunction(flattenAllNodes) ? flattenAllNodes(treeNode) : flattenAllNodes;
×
UNCOV
63
                if (flattenAllNodesFlag || treeNode.isExpanded) {
×
UNCOV
64
                    _getParallelTreeNodes(treeNode.children);
×
UNCOV
65
                }
×
UNCOV
66
            });
×
67
        }
68
        _getParallelTreeNodes(rootTrees);
69
        return flattenTreeData;
70
    }
99✔
71

5,514✔
72
    setCheckStateResolve(resolve: (node: ThyTreeNode) => ThyTreeNodeCheckState = checkStateResolve) {
73
        this.checkStateResolve = resolve;
74
    }
65✔
75

1,358✔
76
    public resetSortedTreeNodes(treeNodes: ThyTreeNode[], parent?: ThyTreeNode) {
77
        treeNodes.forEach(node => {
78
            node.level = node.parentNode ? node.parentNode.level + 1 : 0;
11✔
79
            node.origin.children = node.children.map(n => n.origin);
331✔
80
            node.parentNode = parent;
81
            this.resetSortedTreeNodes(node.children, node);
82
        });
7!
UNCOV
83
    }
×
UNCOV
84

×
UNCOV
85
    public getTreeNode(key: string | number) {
×
UNCOV
86
        const allNodes = this.getParallelTreeNodes(this.treeNodes);
×
UNCOV
87
        return allNodes.find(n => n.key === key);
×
UNCOV
88
    }
×
89

90
    public getExpandedNodes(): ThyTreeNode[] {
91
        const allNodes = this.getParallelTreeNodes(this.treeNodes);
92
        return allNodes.filter(n => n.isExpanded);
12✔
93
    }
7!
94

7✔
95
    public getCheckedNodes(): ThyTreeNode[] {
7✔
96
        const allNodes = this.getParallelTreeNodes(this.treeNodes);
97
        return allNodes.filter(n => n.isChecked === ThyTreeNodeCheckState.checked);
98
    }
99

3✔
100
    public deleteTreeNode(node: ThyTreeNode) {
9✔
101
        if (node.parentNode) {
5✔
102
            const children = node.parentNode.children;
5✔
103
            const index = children.findIndex(n => n.key === node.key);
1✔
104
            if (index > -1) {
105
                children.splice(index, 1);
106
                node.parentNode.origin.children = children.map(item => item.origin);
4✔
107
                this.syncNodeCheckState(node.parentNode);
108
            }
7✔
109
        } else {
5✔
110
            const index = this.treeNodes.findIndex(n => n.key === node.key);
111
            if (index > -1) {
112
                this.treeNodes.splice(index, 1);
4✔
113
                this.originTreeNodes.splice(index, 1);
4✔
114
            }
2✔
115
        }
116
    }
117

2✔
118
    public addTreeNode(node: ThyTreeNode, parent?: ThyTreeNode, index = -1) {
119
        if (parent) {
13✔
120
            const insertNode = new ThyTreeNode(node.origin, parent, this);
121
            if (index > -1) {
122
                parent.children.splice(index, 0, insertNode);
123
            } else {
124
                parent.children.push(insertNode);
60✔
125
            }
60✔
126
            parent.origin.children = parent.children.map(item => item.origin);
6,952✔
127
            this.syncNodeCheckState(parent);
128
        } else {
60✔
129
            const insertNode = new ThyTreeNode(node.origin, null, this);
1,144✔
130
            if (index > -1) {
131
                this.treeNodes.splice(index, 0, insertNode);
60✔
132
            } else {
133
                this.treeNodes.push(insertNode);
134
            }
×
135
            this.originTreeNodes = this.treeNodes.map(item => item.origin);
10✔
136
            // this.syncNodeCheckState(insertNode);
10✔
137
        }
138
    }
×
139

77!
140
    public expandTreeNodes(keyOrKeys: string | number | (string | number)[] | true) {
77✔
141
        const keys = keyOrKeys === true ? [] : coerceArray(keyOrKeys);
67✔
142
        const needExpandNodes = this.getParallelTreeNodes(this.treeNodes).filter(node => {
143
            return keys.indexOf(node.key) > -1 || keyOrKeys === true;
144
        });
77✔
145
        needExpandNodes.forEach(node => {
69✔
146
            node.setExpanded(true);
21✔
147
        });
45✔
148
        this.syncFlattenTreeNodes();
16✔
149
    }
16✔
150

151
    // 设置节点选中状态
152
    public setNodeChecked(node: ThyTreeNode, checked: boolean, propagateUp = true, propagateDown = true): void {
11✔
153
        this._setNodeChecked(node, checked, propagateUp, propagateDown);
5✔
154
        this.syncFlattenTreeNodes();
5✔
155
    }
156

157
    private _setNodeChecked(node: ThyTreeNode, checked: boolean, propagateUp = true, propagateDown = true) {
158
        if (propagateDown && node.children) {
48✔
159
            node.children.forEach(subNode => {
48✔
160
                this._setNodeChecked(subNode, checked, false, true);
161
            });
162
        }
77✔
163
        if (!node.isDisabled) {
10✔
164
            if (node.children.length) {
165
                if (checked) {
166
                    const isAllChildrenChecked = node.children.every(item => item.isChecked === ThyTreeNodeCheckState.checked);
167
                    node.isChecked = isAllChildrenChecked ? ThyTreeNodeCheckState.checked : ThyTreeNodeCheckState.indeterminate;
6✔
168
                    node.origin.checked = isAllChildrenChecked && checked;
6✔
169
                } else {
170
                    const isAllChildrenUnChecked = node.children.every(item => item.isChecked === ThyTreeNodeCheckState.unchecked);
171
                    node.isChecked = isAllChildrenUnChecked ? ThyTreeNodeCheckState.unchecked : ThyTreeNodeCheckState.indeterminate;
37✔
172
                    node.origin.checked = isAllChildrenUnChecked && checked;
21✔
173
                }
21✔
174
            } else {
175
                node.isChecked = checked ? ThyTreeNodeCheckState.checked : ThyTreeNodeCheckState.unchecked;
176
                node.origin.checked = checked;
177
            }
92✔
178
        }
92✔
179
        if (propagateUp) {
180
            this._syncNodeCheckState(node.parentNode);
1✔
181
        }
182
    }
1✔
183

184
    public syncNodeCheckState(node: ThyTreeNode): void {
185
        this._syncNodeCheckState(node);
186
        this.syncFlattenTreeNodes();
187
    }
188

189
    private _syncNodeCheckState(node: ThyTreeNode) {
190
        if (node) {
191
            node.isChecked = this.checkStateResolve(node);
192
            this._syncNodeCheckState(node.parentNode);
193
        }
194
    }
195

196
    ngOnDestroy(): void {
197
        this.statusChange$.complete();
198
        this.statusChange$ = null;
199
    }
200
}
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

© 2025 Coveralls, Inc