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

atinc / ngx-tethys / 5fa9630c-19a1-4ee7-af3d-6a0c3535952a

08 Oct 2024 08:24AM UTC coverage: 90.438% (+0.007%) from 90.431%
5fa9630c-19a1-4ee7-af3d-6a0c3535952a

push

circleci

minlovehua
refactor: refactor all control-flow directives to new control-flow #TINFR-381

5511 of 6738 branches covered (81.79%)

Branch coverage included in aggregate %.

98 of 104 new or added lines in 58 files covered. (94.23%)

113 existing lines in 17 files now uncovered.

13253 of 14010 relevant lines covered (94.6%)

991.73 hits per line

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

92.75
/src/action/action.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    Component,
4
    Input,
5
    OnInit,
6
    AfterViewInit,
7
    OnChanges,
8
    ElementRef,
1✔
9
    Renderer2,
10
    SimpleChanges,
11
    ChangeDetectorRef,
12
    OnDestroy
13
} from '@angular/core';
14
import { useHostRenderer } from '@tethys/cdk/dom';
15
import { ThyIcon } from 'ngx-tethys/icon';
16

17
import { Subscription, timer } from 'rxjs';
18
import { coerceBooleanProperty } from 'ngx-tethys/util';
19

20
export type ThyActionType = 'primary' | 'success' | 'danger' | 'warning';
21

22
export type ThyActionFeedback = 'success' | 'error';
23

24
export interface ThyActionFeedbackOptions {
1✔
25
    icon?: string;
26
    class?: string;
18!
27
    duration?: number;
28
}
29

201✔
30
const defaultFeedbackOptions: Record<ThyActionFeedback, ThyActionFeedbackOptions> = {
31
    success: {
32
        icon: 'check-circle-fill',
231✔
33
        class: 'text-success',
34
        duration: 3000
35
    },
9✔
36
    error: {
37
        icon: 'close-circle-fill',
UNCOV
38
        class: 'text-danger',
×
39
        duration: 3000
40
    }
41
};
434✔
42
/**
434✔
43
 * 立即操作组件
434✔
44
 * @name thy-action,[thyAction]
434✔
45
 */
434✔
46
@Component({
434✔
47
    selector: 'thy-action, [thyAction]',
434✔
48
    templateUrl: './action.component.html',
434✔
49
    changeDetection: ChangeDetectionStrategy.OnPush,
50
    host: {
51
        class: 'thy-action',
434✔
52
        '[class.active]': 'active',
53
        '[class.thy-action-hover-icon]': 'thyHoverIcon',
54
        '[class.thy-action-has-feedback]': '!!feedback',
434✔
55
        '[class.disabled]': 'thyDisabled'
56
    },
57
    standalone: true,
434!
NEW
58
    imports: [ThyIcon]
×
59
})
60
export class ThyAction implements OnInit, AfterViewInit, OnChanges, OnDestroy {
61
    icon: string;
62

154✔
63
    feedback: ThyActionFeedback = null;
64

65
    feedbackOptions: ThyActionFeedbackOptions;
66

67
    active = false;
68

2✔
69
    private type: string = 'primary';
70

71
    private hostRenderer = useHostRenderer();
72

73
    private feedbackTimer: Subscription;
74

1✔
75
    /**
76
     * 操作图标的类型
77
     * @type primary | success | danger | warning
3✔
78
     * @default primary
1✔
79
     */
80
    @Input()
2✔
81
    set thyType(value: ThyActionType) {
2✔
82
        this.setActionType(value || 'primary');
2✔
83
    }
2✔
84

2!
85
    /**
2✔
86
     * 操作图标,支持传参同时也支持在投影中写 thy-icon 组件
1✔
87
     */
88
    @Input()
2✔
89
    set thyIcon(icon: string) {
1✔
90
        this.icon = icon;
1✔
91
    }
1✔
92

93
    /**
94
     * 操作图标,当 thyIcon 和其他指令参数名有冲突时使用 thyActionIcon
95
     */
96
    @Input()
434✔
97
    set thyActionIcon(icon: string) {
1,763✔
98
        this.icon = icon;
9✔
99
    }
9✔
100

101
    /**
9✔
102
     * 操作的图标 Active 状态,设置为 true 时会在 Item 上添加 active class
9✔
103
     * @default false
104
     */
105
    @Input({ transform: coerceBooleanProperty })
106
    set thyActive(value: boolean) {
107
        this.active = value;
18✔
108
    }
109

110
    /**
434✔
111
     * 操作的图标 Active 状态,当 thyActive 和其他指令参数名有冲突时使用 thyActionActive
434✔
112
     * @default false
434✔
113
     */
9✔
114
    @Input({ transform: coerceBooleanProperty })
115
    set thyActionActive(value: boolean) {
434✔
116
        this.active = value;
117
    }
118

434✔
119
    /**
120
     * 操作图标的主题
1✔
121
     * @type fill(背景色填充) | lite(简单文本颜色变化)
122
     */
123
    @Input() thyTheme: 'fill' | 'lite' = 'fill';
124

125
    /**
1✔
126
     * Hover 展示的图标
127
     */
128
    @Input() thyHoverIcon: string;
129

130
    /**
131
     * 是否处于禁用状态
132
     * @default false
133
     */
134
    @Input({ transform: coerceBooleanProperty })
135
    thyDisabled: boolean;
136

1✔
137
    constructor(
138
        private elementRef: ElementRef<HTMLElement>,
139
        private renderer: Renderer2,
140
        private cdr: ChangeDetectorRef
141
    ) {}
142

143
    ngOnInit(): void {
144
        this.updateClasses();
145
    }
146

147
    ngAfterViewInit() {
148
        this.wrapSpanForText(this.elementRef.nativeElement.childNodes);
149
    }
150

151
    ngOnChanges(changes: SimpleChanges): void {
152
        if ((changes.thyType && !changes.thyType.firstChange) || (changes.thyTheme && !changes.thyTheme.firstChange)) {
153
            this.updateClasses();
154
        }
155
    }
156

157
    setMarginRight(marginRight: string) {
158
        this.elementRef.nativeElement.style.marginRight = marginRight;
159
    }
160

161
    /**
162
     * 触发成功反馈操作
163
     */
164
    success(options?: ThyActionFeedbackOptions) {
165
        this.setFeedback('success', options);
166
    }
167

168
    /**
169
     * 触发失败反馈操作
170
     */
171
    error(options?: ThyActionFeedbackOptions) {
172
        this.setFeedback('error', options);
173
    }
174

175
    private setFeedback(feedback: ThyActionFeedback, options: ThyActionFeedbackOptions) {
176
        if (this.thyDisabled) {
177
            return;
178
        }
179
        options = Object.assign({}, defaultFeedbackOptions[feedback], options);
180
        this.feedback = feedback;
181
        this.feedbackOptions = options;
182
        this.cdr.markForCheck();
183
        if (options.duration) {
184
            if (this.feedbackTimer) {
185
                this.feedbackTimer.unsubscribe();
186
            }
187
            this.feedbackTimer = timer(options.duration).subscribe(() => {
188
                this.feedback = null;
189
                this.feedbackOptions = null;
190
                this.cdr.markForCheck();
191
            });
192
        }
193
    }
194

195
    private wrapSpanForText(nodes: NodeList): void {
196
        nodes.forEach(node => {
197
            if (node.nodeName === '#text') {
198
                const span = this.renderer.createElement('span');
199
                const parent = this.renderer.parentNode(node);
200
                // this.renderer.addClass(span, 'thy-action-wrap-span');
201
                this.renderer.insertBefore(parent, span, node);
202
                this.renderer.appendChild(span, node);
203
            }
204
        });
205
    }
206

207
    private setActionType(value: ThyActionType) {
208
        this.type = value;
209
    }
210

211
    private updateClasses() {
212
        let classNames: string[] = [];
213
        classNames.push(`action-${this.type}`);
214
        if (this.thyTheme === 'lite') {
215
            classNames.push('thy-action-lite');
216
        }
217
        this.hostRenderer.updateClass(classNames);
218
    }
219

220
    ngOnDestroy(): void {
221
        this.feedbackTimer?.unsubscribe();
222
    }
223
}
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