• 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

93.75
/src/rate/rate.component.ts
1
import { TabIndexDisabledControlValueAccessorMixin } from 'ngx-tethys/core';
2
import { helpers } from 'ngx-tethys/util';
3

4
import { NgClass, NgFor } from '@angular/common';
5
import {
6
    booleanAttribute,
7
    ChangeDetectionStrategy,
8
    ChangeDetectorRef,
9
    Component,
10
    EventEmitter,
11
    forwardRef,
1✔
12
    HostBinding,
13
    Input,
14
    numberAttribute,
15
    OnChanges,
16
    OnInit,
17
    Output,
1✔
18
    SimpleChanges,
19
    TemplateRef
3✔
20
} from '@angular/core';
21
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
22
import { ThyStopPropagationDirective } from 'ngx-tethys/shared';
187✔
23
import { ThyTooltipDirective } from 'ngx-tethys/tooltip';
24
import { ThyRateItem } from './rate-item.component';
25

6✔
26
const noop = () => {};
6✔
27

2✔
28
/**
2✔
29
 * 评分组件
30
 * @name thy-rate
31
 * @order 10
4✔
32
 */
33
@Component({
34
    selector: 'thy-rate',
35
    templateUrl: './rate.component.html',
41✔
36
    providers: [
41✔
37
        {
41✔
38
            provide: NG_VALUE_ACCESSOR,
41✔
39
            useExisting: forwardRef(() => ThyRate),
41✔
40
            multi: true
41✔
41
        }
41✔
42
    ],
41✔
43
    host: {
41✔
44
        '[attr.tabindex]': `tabIndex`,
41✔
45
        class: 'thy-rate'
41✔
46
    },
41✔
47
    changeDetection: ChangeDetectionStrategy.OnPush,
41✔
48
    standalone: true,
41✔
49
    imports: [NgFor, ThyStopPropagationDirective, ThyRateItem, NgClass, ThyTooltipDirective]
41✔
50
})
41✔
51
export class ThyRate extends TabIndexDisabledControlValueAccessorMixin implements ControlValueAccessor, OnInit, OnChanges {
41✔
52
    private _value = 0;
41✔
53

41✔
54
    private currentValue = 0;
55

56
    private hasHalf = false;
10✔
57

58
    public rateArray: number[] = [];
59

33✔
60
    public rateStyleArray: Record<string, boolean>[] = [];
18✔
61

62
    private icons: string | TemplateRef<any> | string[] | TemplateRef<any>[] = null;
15✔
63

15✔
64
    public iconValue: string = null;
15✔
65

66
    public iconTemplate: TemplateRef<any> = null;
67

30✔
68
    private onTouchedCallback: () => void = noop;
30✔
69

30✔
70
    private onChangeCallback: (_: any) => void = noop;
71

72
    /**
73
     * 自定义评分的总数
16✔
74
     */
16✔
75
    @Input({ transform: numberAttribute }) thyCount = 5;
1✔
76

77
    /**
16!
UNCOV
78
     * 是否只读
×
79
     * @default false
80
     */
16✔
81
    @Input({ transform: booleanAttribute })
82
    override set thyDisabled(value: boolean) {
83
        this.disabled = value;
7!
NEW
84
    }
×
85
    override get thyDisabled(): boolean {
86
        return this.disabled;
7✔
87
    }
7✔
88

7!
89
    disabled = false;
7✔
90

7✔
91
    /**
92
     * 是否允许半选
93
     * @default false
4✔
94
     */
1✔
95
    @Input({ transform: booleanAttribute }) thyAllowHalf = false;
96

3✔
97
    /**
3!
98
     * 是否允许再次点击后清除
3✔
99
     */
1!
100
    @Input({ transform: booleanAttribute }) thyAllowClear = true;
1✔
101

1✔
102
    /**
1✔
103
     * 自定义每项的提示信息
104
     * @type string[]
105
     */
106
    @Input() thyTooltips: string[] = [];
2✔
107

2✔
108
    /**
2✔
109
     * 自定义模板,目前支持传单个模板或图标名称、数组(模板 | 图标名称)
110
     * @type string | TemplateRef<any> | string[] | TemplateRef<any>[]
3✔
111
     */
112
    @Input('thyIconTemplate')
113
    set thyIconTemplate(value: string | TemplateRef<any> | string[] | TemplateRef<any>[]) {
2✔
114
        this.icons = value;
2✔
115
        if (!this.icons) {
2✔
116
            this.iconValue = null;
2✔
117
            this.iconTemplate = null;
118
        } else {
119
            this.setIconTemplate();
31✔
120
        }
121
    }
122

149✔
123
    /**
124
     * 当前值hover时的回调
31✔
125
     */
126
    @Output() readonly thyItemHoverChange = new EventEmitter<number>();
127

43✔
128
    @HostBinding('class.thy-rate') className = true;
43✔
129

43✔
130
    constructor(private cdr: ChangeDetectorRef) {
209✔
131
        super();
209✔
132
    }
406✔
133

219✔
134
    get thyValue(): number {
219✔
135
        return this._value;
136
    }
137

138
    set thyValue(value: number) {
139
        if (this._value === value) {
140
            return;
43✔
141
        }
29✔
142
        this._value = value;
29✔
143
        this.hasHalf = !Number.isInteger(value);
144
        this.currentValue = Math.ceil(value);
145
    }
14✔
146

147
    writeValue(value: number): void {
148
        this.thyValue = value || 0;
149
        this.updateRateArray();
18✔
150
        this.cdr.markForCheck();
13✔
151
    }
13✔
152

8✔
153
    ngOnInit() {}
154

155
    ngOnChanges(changes: SimpleChanges): void {
5✔
156
        const { thyCount, thyValue } = changes;
157
        if (thyCount) {
158
            this.updateRateArray();
5!
159
        }
5✔
160

2✔
161
        if (thyValue) {
162
            this.updateItemStyle();
163
        }
3✔
164
        this.cdr.detectChanges();
165
    }
166

167
    itemHover(isHalf: boolean, index: number): void {
168
        if (this.thyDisabled || (this.currentValue === index + 1 && this.hasHalf === isHalf)) {
14✔
169
            return;
170
        }
171
        this.currentValue = index + 1;
14✔
172
        this.hasHalf = isHalf;
173
        const _value = isHalf ? Number(this.currentValue - 0.5) : this.currentValue;
174
        this.thyItemHoverChange.emit(_value);
224✔
175
        this.updateItemStyle();
176
    }
1✔
177

178
    itemClick(isHalf: boolean, index: number) {
179
        if (this.thyDisabled) {
1✔
180
            return;
181
        }
182
        this.currentValue = index + 1;
183
        const _value = isHalf ? index + 1 - 0.5 : index + 1;
184
        if (this.thyValue === _value) {
185
            if (this.thyAllowClear) {
186
                this.thyValue = 0;
187
                this.onChangeCallback(this.thyValue);
188
                this.onTouchedCallback();
189
            }
190
        } else {
1✔
191
            this.thyValue = _value;
192
            this.onChangeCallback(this.thyValue);
193
            this.onTouchedCallback();
194
        }
195
        this.updateItemStyle();
196
    }
197

41✔
198
    onRateLeave(event: Event): void {
199
        event.stopPropagation();
200
        this.hasHalf = !Number.isInteger(this.thyValue);
201
        this.currentValue = Math.ceil(this.thyValue);
202
        this.updateItemStyle();
203
    }
204

205
    updateRateArray(): void {
206
        this.rateArray = Array(this.thyCount)
207
            .fill(0)
208
            .map((_, i) => {
209
                return i;
210
            });
211
        this.updateItemStyle();
212
    }
213

214
    updateItemStyle(): void {
215
        this.updateIcon();
216
        const rateStyle = 'thy-rate-star';
217
        this.rateStyleArray = this.rateArray.map(i => {
218
            const value = i + 1;
219
            return {
220
                [`${rateStyle}--full`]: value < this.currentValue || (value === this.currentValue && !this.hasHalf),
221
                [`${rateStyle}--half`]: this.hasHalf && value === this.currentValue,
222
                [`${rateStyle}--active`]: this.hasHalf && value === this.currentValue,
223
                [`${rateStyle}--zero`]: value > this.currentValue
224
            };
225
        });
226
    }
227

228
    updateIcon(): void {
229
        if (!this.icons) {
230
            this.iconValue = null;
231
            this.iconTemplate = null;
232
        } else {
233
            this.setIconTemplate();
234
        }
235
    }
236

237
    setIconTemplate(): void {
238
        if (helpers.isArray(this.icons) && this.icons.length > 0) {
239
            const currentIcon = (this.currentValue && this.currentValue - 1) || 0;
240
            if (this.icons[currentIcon] instanceof TemplateRef) {
241
                this.iconTemplate = this.icons[currentIcon] as TemplateRef<any>;
242
            } else {
243
                this.iconValue = this.icons[currentIcon] as string;
244
            }
245
        } else if (!helpers.isArray(this.icons)) {
246
            if (this.icons instanceof TemplateRef) {
247
                this.iconTemplate = this.icons;
248
            } else {
249
                this.iconValue = this.icons;
250
            }
251
        }
252
    }
253

254
    registerOnChange(fn: any): void {
255
        this.onChangeCallback = fn;
256
    }
257

258
    registerOnTouched(fn: any): void {
259
        this.onTouchedCallback = fn;
260
    }
261

262
    trackByFn(index: number, item: any) {
263
        return item || index;
264
    }
265
}
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