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

atinc / ngx-tethys / e62d3b10-1466-49c3-aabd-707148681fc8

14 Jun 2024 08:24AM UTC coverage: 90.422%. Remained the same
e62d3b10-1466-49c3-aabd-707148681fc8

push

circleci

minlovehua
feat: use the ngx-tethys/util's coerceBooleanProperty instead of booleanAttribute #INFR-12648

5467 of 6692 branches covered (81.69%)

Branch coverage included in aggregate %.

117 of 120 new or added lines in 66 files covered. (97.5%)

183 existing lines in 46 files now uncovered.

13216 of 13970 relevant lines covered (94.6%)

985.91 hits per line

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

93.83
/src/tree/tree-node.component.ts
1
import { Subject } from 'rxjs';
2
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
3
import {
4
    Component,
5
    ContentChild,
6
    ElementRef,
7
    EventEmitter,
8
    HostBinding,
9
    Inject,
10
    Input,
11
    OnChanges,
12
    OnDestroy,
13
    OnInit,
14
    Output,
1✔
15
    SimpleChanges,
16
    TemplateRef,
17
    ViewChild,
18
    ViewEncapsulation,
19
    numberAttribute
20
} from '@angular/core';
1✔
21

22
import { THY_TREE_ABSTRACT_TOKEN, ThyTreeAbstractComponent } from './tree-abstract';
199✔
23
import { ThyTreeNode } from './tree-node.class';
24
import { ThyTreeEmitEvent, ThyTreeNodeCheckState, ThyClickBehavior } from './tree.class';
UNCOV
25
import { ThyTreeService } from './tree.service';
×
26
import { ThyLoading } from 'ngx-tethys/loading';
27
import { ThyIcon } from 'ngx-tethys/icon';
28
import { NgIf, NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
623✔
29
import { coerceBooleanProperty } from 'ngx-tethys/util';
623✔
30

623✔
31
const passiveEventListenerOptions = <AddEventListenerOptions>normalizePassiveListenerOptions({ passive: true });
623✔
32

623✔
33
/**
623✔
34
 * 树形控件的节点组件
623✔
35
 * @private
623✔
36
 * @name thy-tree-node
623✔
37
 */
623✔
38
@Component({
623✔
39
    selector: 'thy-tree-node',
623✔
40
    templateUrl: './tree-node.component.html',
623✔
41
    encapsulation: ViewEncapsulation.None,
623✔
42
    standalone: true,
623✔
43
    imports: [NgIf, ThyIcon, NgClass, NgStyle, NgTemplateOutlet, ThyLoading]
44
})
45
export class ThyTreeNodeComponent implements OnDestroy, OnInit, OnChanges {
7✔
46
    /**
2✔
47
     * node 节点展现所需的数据
48
     */
49
    @Input() node: ThyTreeNode;
5✔
50

2✔
51
    /**
52
     * 设置 TreeNode 是否支持异步加载
53
     */
3✔
54
    @Input({ transform: coerceBooleanProperty }) thyAsync = false;
1✔
55

56
    /**
57
     * 设置 TreeNode 是否支持多选节点
2✔
58
     */
59
    @Input({ transform: coerceBooleanProperty }) thyMultiple = false;
60

61
    /**
7✔
62
     * 设置 TreeNode 是否支持拖拽排序
63
     */
64
    @Input({ transform: coerceBooleanProperty }) thyDraggable = false;
65

66
    /**
67
     * 设置 TreeNode 是否支持 Checkbox 选择
68
     */
1✔
69
    @Input({ transform: coerceBooleanProperty }) thyCheckable = false;
70

71
    /**
72
     * 点击节点的行为,`default` 为选中当前节点,`selectCheckbox` 为选中节点的 Checkbox, `thyCheckable` 为 true 时生效。
73
     * @default default
74
     */
75
    @Input() thyClickBehavior: ThyClickBehavior;
9✔
76

9✔
77
    /**
5✔
78
     * 设置节点名称是否支持超出截取
79
     * @default false
4✔
80
     */
1✔
81
    @Input({ transform: coerceBooleanProperty }) thyTitleTruncate: boolean;
82

3!
83
    /**
3!
84
     * 设置 TreeNode 的渲染模板
16✔
85
     */
5✔
86
    @Input() templateRef: TemplateRef<any>;
3✔
87

88
    /**
UNCOV
89
     * 设置子的空数据渲染模板
×
90
     */
91
    @Input() emptyChildrenTemplateRef: TemplateRef<any>;
92

9✔
93
    /**
94
     * 设置 node 点击事件
95
     */
96
    @Output() thyOnClick: EventEmitter<ThyTreeEmitEvent> = new EventEmitter<ThyTreeEmitEvent>();
97

98
    /**
99
     * 双击 node 事件
6✔
100
     */
6✔
101
    @Output() thyDblClick: EventEmitter<ThyTreeEmitEvent> = new EventEmitter<ThyTreeEmitEvent>();
6!
102

6✔
103
    /**
104
     * 点击展开触发事件
105
     */
106
    @Output() thyOnExpandChange: EventEmitter<ThyTreeEmitEvent> = new EventEmitter<ThyTreeEmitEvent>();
107

6✔
108
    /**
1✔
109
     * 设置 check 选择事件
110
     */
111
    @Output() thyOnCheckboxChange: EventEmitter<ThyTreeEmitEvent> = new EventEmitter<ThyTreeEmitEvent>();
112

113
    /**
1,034✔
114
     * 设置 childrenTree 的渲染模板
115
     */
116
    @ContentChild('childrenTree') childrenTreeTemplateRef: TemplateRef<any>;
623✔
117

118
    /** The native `<div class="thy-tree-node-wrapper thy-sortable-item"></div>` element. */
119
    @ViewChild('treeNodeWrapper', { static: true }) treeNodeWrapper: ElementRef<HTMLElement>;
706✔
120

59✔
121
    @HostBinding('class.thy-tree-node') thyTreeNodeClass = true;
122

123
    @HostBinding('class') itemClass: string;
124

623✔
125
    /**
623✔
126
     * 开启虚拟滚动时,单行节点的高度,当`thySize`为`default`时,该参数才生效
127
     */
1✔
128
    @Input({ transform: numberAttribute }) thyItemSize = 44;
129

130
    /**
131
     * 设置节点缩进距离,缩进距离 = thyIndent * node.level
1✔
132
     */
133
    @Input({ transform: numberAttribute }) thyIndent = 25;
134

135
    public get nodeIcon() {
136
        return this.node.origin.icon;
137
    }
138

139
    public get nodeIconStyle() {
140
        return this.node.origin.iconStyle;
141
    }
142

143
    private destroy$ = new Subject<void>();
144

145
    checkState = ThyTreeNodeCheckState;
146

147
    constructor(@Inject(THY_TREE_ABSTRACT_TOKEN) public root: ThyTreeAbstractComponent, public thyTreeService: ThyTreeService) {}
148

149
    public clickNode(event: Event) {
150
        if (this.node.isDisabled) {
151
            this.expandNode(event);
152
        } else {
153
            if (this.thyCheckable && this.thyClickBehavior === 'selectCheckbox') {
1✔
154
                this.clickNodeCheck(event);
155
            } else {
156
                if (this.root.thyMultiple) {
157
                    this.root.toggleTreeNode(this.node);
158
                } else {
159
                    this.root.selectTreeNode(this.node);
160
                }
161
            }
162
        }
163
        this.thyOnClick.emit({
164
            eventName: 'click',
165
            event: event,
166
            node: this.node
167
        });
168
    }
169

170
    public dbClickNode(event: Event) {
171
        this.thyDblClick.emit({
172
            eventName: 'dbclick',
173
            event: event,
174
            node: this.node
175
        });
176
    }
177

178
    public clickNodeCheck(event: Event) {
179
        event.stopPropagation();
180
        if (this.node.isChecked === ThyTreeNodeCheckState.unchecked) {
181
            this.node.setChecked(true);
182
        } else if (this.node.isChecked === ThyTreeNodeCheckState.checked) {
183
            this.node.setChecked(false);
184
        } else if (this.node.isChecked === ThyTreeNodeCheckState.indeterminate) {
185
            if (this.node.children?.length) {
186
                const activeChildren = this.node.children.filter(item => !item.isDisabled);
187
                const isAllActiveChildrenChecked = activeChildren.every(item => item.isChecked);
188
                this.node.setChecked(!isAllActiveChildrenChecked);
189
            } else {
190
                this.node.setChecked(true);
191
            }
192
        }
193
        this.thyOnCheckboxChange.emit({
194
            eventName: 'checkboxChange',
195
            event: event,
196
            node: this.node
197
        });
198
    }
199

200
    public expandNode(event: Event) {
201
        event.stopPropagation();
202
        this.node.setExpanded(!this.node.isExpanded);
203
        if (this.root.thyShowExpand) {
204
            this.thyOnExpandChange.emit({
205
                eventName: 'expand',
206
                event: event,
207
                node: this.node
208
            });
209
            if (this.thyAsync && this.node.children.length === 0) {
210
                this.node.setLoading(true);
211
            }
212
        }
213
    }
214

215
    public isShowExpand(node: ThyTreeNode) {
216
        return this.root.isShowExpand(node);
217
    }
218

219
    ngOnInit(): void {
220
        this.itemClass = this.node?.itemClass?.join(' ');
221
    }
222

223
    ngOnChanges(changes: SimpleChanges): void {
224
        if (changes.node && !changes.node.isFirstChange()) {
225
            this.itemClass = changes?.node?.currentValue.itemClass?.join(' ');
226
        }
227
    }
228

229
    ngOnDestroy(): void {
230
        this.destroy$.next();
231
        this.destroy$.complete();
232
    }
233
}
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