• 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

92.75
/src/action/action.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    Component,
4
    Input,
5
    OnInit,
6
    AfterViewInit,
7
    OnChanges,
8
    ElementRef,
9
    Renderer2,
1✔
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
import { NgIf } from '@angular/common';
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 {
25
    icon?: string;
1✔
26
    class?: string;
27
    duration?: number;
18!
28
}
29

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

63
    feedback: ThyActionFeedback = null;
153✔
64

65
    feedbackOptions: ThyActionFeedbackOptions;
66

67
    active = false;
68

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

71
    private hostRenderer = useHostRenderer();
72

73
    private feedbackTimer: Subscription;
74

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

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

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

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

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

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

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

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

137
    constructor(private elementRef: ElementRef<HTMLElement>, private renderer: Renderer2, private cdr: ChangeDetectorRef) {}
1✔
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