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

atinc / ngx-tethys / edbc1d43-1648-411a-a6bc-f24c9aa3f654

27 Mar 2025 06:13AM UTC coverage: 90.236% (+0.06%) from 90.179%
edbc1d43-1648-411a-a6bc-f24c9aa3f654

push

circleci

web-flow
Merge pull request #3282 from atinc/v19.0.0-next

5598 of 6865 branches covered (81.54%)

Branch coverage included in aggregate %.

8 of 8 new or added lines in 7 files covered. (100.0%)

157 existing lines in 46 files now uncovered.

13357 of 14141 relevant lines covered (94.46%)

992.51 hits per line

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

93.75
/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
    Input,
10
    OnChanges,
11
    OnDestroy,
12
    OnInit,
13
    Output,
14
    SimpleChanges,
1✔
15
    TemplateRef,
16
    ViewChild,
17
    ViewEncapsulation,
18
    numberAttribute,
19
    inject
20
} from '@angular/core';
1✔
21

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

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

657✔
33
/**
657✔
34
 * 树形控件的节点组件
657✔
35
 * @private
657✔
36
 * @name thy-tree-node
657✔
37
 */
38
@Component({
39
    selector: 'thy-tree-node',
205✔
40
    templateUrl: './tree-node.component.html',
41
    encapsulation: ViewEncapsulation.None,
42
    imports: [ThyIcon, NgClass, NgStyle, NgTemplateOutlet, ThyLoading]
×
43
})
44
export class ThyTreeNodeComponent implements OnDestroy, OnInit, OnChanges {
45
    root = inject(THY_TREE_ABSTRACT_TOKEN);
7✔
46
    thyTreeService = inject(ThyTreeService);
2✔
47

48
    /**
49
     * node 节点展现所需的数据
5✔
50
     */
2✔
51
    @Input() node: ThyTreeNode;
52

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

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

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

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

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

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

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

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

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

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

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

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

115
    /**
116
     * 设置 childrenTree 的渲染模板
657✔
117
     */
118
    @ContentChild('childrenTree') childrenTreeTemplateRef: TemplateRef<any>;
119

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

123
    @HostBinding('class.thy-tree-node') thyTreeNodeClass = true;
124

657✔
125
    @HostBinding('class') itemClass: string;
657✔
126

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

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

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

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

145
    private destroy$ = new Subject<void>();
146

147
    checkState = ThyTreeNodeCheckState;
148

149
    public clickNode(event: Event) {
1✔
150
        if (this.node.isDisabled) {
151
            this.expandNode(event);
152
        } else {
153
            if (this.thyCheckable && this.thyClickBehavior === 'selectCheckbox') {
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