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

atinc / ngx-tethys / 881c8997-29c3-4d01-9ef1-22092f16cec2

03 Apr 2024 03:31AM UTC coverage: 90.404% (-0.2%) from 90.585%
881c8997-29c3-4d01-9ef1-22092f16cec2

Pull #3062

circleci

minlovehua
refactor(all): use the transform attribute of @input() instead of @InputBoolean() and @InputNumber()
Pull Request #3062: refactor(all): use the transform attribute of @input() instead of @InputBoolean() and @InputNumber()

5411 of 6635 branches covered (81.55%)

Branch coverage included in aggregate %.

217 of 223 new or added lines in 82 files covered. (97.31%)

201 existing lines in 53 files now uncovered.

13176 of 13925 relevant lines covered (94.62%)

980.1 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
    booleanAttribute
14
} from '@angular/core';
15
import { useHostRenderer } from '@tethys/cdk/dom';
16
import { ThyIcon } from 'ngx-tethys/icon';
17
import { NgIf } from '@angular/common';
18
import { Subscription, timer } from 'rxjs';
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',
226✔
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
};
429✔
42
/**
429✔
43
 * 立即操作组件
429✔
44
 * @name thy-action,[thyAction]
429✔
45
 */
429✔
46
@Component({
429✔
47
    selector: 'thy-action, [thyAction]',
429✔
48
    templateUrl: './action.component.html',
429✔
49
    changeDetection: ChangeDetectionStrategy.OnPush,
50
    host: {
51
        class: 'thy-action',
429✔
52
        '[class.active]': 'active',
53
        '[class.thy-action-hover-icon]': 'thyHoverIcon',
54
        '[class.thy-action-has-feedback]': '!!feedback',
429✔
55
        '[class.disabled]': 'thyDisabled'
56
    },
57
    standalone: true,
429!
UNCOV
58
    imports: [NgIf, ThyIcon]
×
59
})
60
export class ThyAction implements OnInit, AfterViewInit, OnChanges, OnDestroy {
61
    icon: string;
62

153✔
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()
429✔
97
    set thyActionIcon(icon: string) {
1,743✔
98
        this.icon = icon;
9✔
99
    }
9✔
100

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

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

429✔
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: booleanAttribute })
135
    thyDisabled: boolean;
136

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

139
    ngOnInit(): void {
140
        this.updateClasses();
141
    }
142

143
    ngAfterViewInit() {
144
        this.wrapSpanForText(this.elementRef.nativeElement.childNodes);
145
    }
146

147
    ngOnChanges(changes: SimpleChanges): void {
148
        if ((changes.thyType && !changes.thyType.firstChange) || (changes.thyTheme && !changes.thyTheme.firstChange)) {
149
            this.updateClasses();
150
        }
151
    }
152

153
    setMarginRight(marginRight: string) {
154
        this.elementRef.nativeElement.style.marginRight = marginRight;
155
    }
156

157
    /**
158
     * 触发成功反馈操作
159
     */
160
    success(options?: ThyActionFeedbackOptions) {
161
        this.setFeedback('success', options);
162
    }
163

164
    /**
165
     * 触发失败反馈操作
166
     */
167
    error(options?: ThyActionFeedbackOptions) {
168
        this.setFeedback('error', options);
169
    }
170

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

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

203
    private setActionType(value: ThyActionType) {
204
        this.type = value;
205
    }
206

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

216
    ngOnDestroy(): void {
217
        this.feedbackTimer?.unsubscribe();
218
    }
219
}
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