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

atinc / ngx-tethys / 68ef226c-f83e-44c1-b8ed-e420a83c5d84

28 May 2025 10:31AM UTC coverage: 10.352% (-80.0%) from 90.316%
68ef226c-f83e-44c1-b8ed-e420a83c5d84

Pull #3460

circleci

pubuzhixing8
chore: xxx
Pull Request #3460: refactor(icon): migrate signal input #TINFR-1476

132 of 6823 branches covered (1.93%)

Branch coverage included in aggregate %.

10 of 14 new or added lines in 1 file covered. (71.43%)

11648 existing lines in 344 files now uncovered.

2078 of 14525 relevant lines covered (14.31%)

6.69 hits per line

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

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

4
import { NgClass } from '@angular/common';
5
import {
6
    ChangeDetectionStrategy,
7
    ChangeDetectorRef,
8
    Component,
9
    forwardRef,
10
    Input,
11
    numberAttribute,
12
    TemplateRef,
1✔
13
    inject,
14
    input,
15
    output,
16
    computed,
17
    model,
18
    ModelSignal,
1✔
19
    effect
UNCOV
20
} from '@angular/core';
×
21
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
22
import { ThyStopPropagationDirective } from 'ngx-tethys/shared';
UNCOV
23
import { ThyTooltipDirective } from 'ngx-tethys/tooltip';
×
24
import { ThyRateItem } from './rate-item.component';
25

UNCOV
26
const noop = () => {};
×
UNCOV
27

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

×
UNCOV
53
    readonly thyValue: ModelSignal<number> = model(0);
×
54

UNCOV
55
    readonly currentValue: ModelSignal<number> = model(0);
×
UNCOV
56

×
57
    readonly hasHalf: ModelSignal<boolean> = model(false);
58

UNCOV
59
    private onTouchedCallback: () => void = noop;
×
60

61
    private onChangeCallback: (_: any) => void = noop;
62

UNCOV
63
    /**
×
UNCOV
64
     * 自定义评分的总数
×
UNCOV
65
     */
×
UNCOV
66
    readonly thyCount = input(5, { transform: numberAttribute });
×
UNCOV
67

×
68
    /**
69
     * 是否只读
UNCOV
70
     * @default false
×
UNCOV
71
     */
×
UNCOV
72
    @Input({ transform: coerceBooleanProperty })
×
UNCOV
73
    override set thyDisabled(value: boolean) {
×
74
        this.disabled = value;
UNCOV
75
    }
×
UNCOV
76
    override get thyDisabled(): boolean {
×
77
        return this.disabled;
UNCOV
78
    }
×
UNCOV
79

×
80
    disabled = false;
81

UNCOV
82
    /**
×
83
     * 是否允许半选
84
     * @default false
85
     */
UNCOV
86
    readonly thyAllowHalf = input<boolean, ThyBooleanInput>(false, { transform: coerceBooleanProperty });
×
UNCOV
87

×
88
    /**
UNCOV
89
     * 是否允许再次点击后清除
×
UNCOV
90
     */
×
91
    readonly thyAllowClear = input<boolean, ThyBooleanInput>(true, { transform: coerceBooleanProperty });
UNCOV
92

×
UNCOV
93
    /**
×
UNCOV
94
     * 自定义每项的提示信息
×
95
     * @type string[]
96
     */
97
    readonly thyTooltips = input<string[]>([]);
UNCOV
98

×
UNCOV
99
    /**
×
100
     * 自定义模板,目前支持传单个模板或图标名称、数组(模板 | 图标名称)
101
     * @type string | TemplateRef<any> | string[] | TemplateRef<any>[]
UNCOV
102
     */
×
103
    readonly thyIconTemplate = input<string | TemplateRef<any> | string[] | TemplateRef<any>[]>(null);
×
104

UNCOV
105
    /**
×
UNCOV
106
     * 当前值hover时的回调
×
UNCOV
107
     */
×
UNCOV
108
    readonly thyItemHoverChange = output<number>();
×
109

110
    readonly iconValue = computed(() => {
UNCOV
111
        const icons = this.thyIconTemplate();
×
UNCOV
112
        const currentValue = this.currentValue();
×
113
        if (!icons) {
UNCOV
114
            return null;
×
UNCOV
115
        } else {
×
UNCOV
116
            let iconValue = null;
×
UNCOV
117
            if (helpers.isArray(icons) && icons.length > 0) {
×
UNCOV
118
                const currentIcon = (currentValue && currentValue - 1) || 0;
×
UNCOV
119
                iconValue = icons[currentIcon];
×
UNCOV
120
            } else if (!helpers.isArray(icons)) {
×
121
                iconValue = icons;
122
            }
123
            if (iconValue instanceof TemplateRef) {
UNCOV
124
                return null;
×
UNCOV
125
            } else {
×
UNCOV
126
                return iconValue;
×
127
            }
128
        }
129
    });
UNCOV
130

×
UNCOV
131
    readonly iconTemplate = computed(() => {
×
UNCOV
132
        const icons = this.thyIconTemplate();
×
133
        const currentValue = this.currentValue();
134
        if (!icons) {
UNCOV
135
            return null;
×
136
        } else {
137
            let iconTemplate = null;
138
            if (helpers.isArray(icons) && icons.length > 0) {
UNCOV
139
                const currentIcon = (currentValue && currentValue - 1) || 0;
×
140
                iconTemplate = icons[currentIcon] as TemplateRef<any>;
141
            } else if (!helpers.isArray(icons)) {
142
                iconTemplate = icons;
143
            }
UNCOV
144
            if (iconTemplate instanceof TemplateRef) {
×
UNCOV
145
                return iconTemplate;
×
UNCOV
146
            } else {
×
UNCOV
147
                return null;
×
148
            }
×
149
        }
×
150
    });
×
151

152
    readonly rateArray = computed(() => {
153
        return this.updateRateArray();
154
    });
155

UNCOV
156
    readonly rateStyleArray = computed(() => {
×
157
        return this.updateItemStyle();
158
    });
UNCOV
159

×
160
    constructor() {
161
        super();
UNCOV
162
        effect(() => {
×
163
            this.hasHalf.set(!Number.isInteger(this.thyValue()));
164
            this.currentValue.set(Math.ceil(this.thyValue()));
1✔
165
        });
1✔
166
    }
167

168
    writeValue(value: number): void {
169
        this.thyValue.set(value || 0);
170
        this.cdr.markForCheck();
171
    }
172

173
    itemHover(isHalf: boolean, index: number): void {
174
        if (this.thyDisabled || (this.currentValue() === index + 1 && this.hasHalf() === isHalf)) {
175
            return;
176
        }
177
        this.currentValue.set(index + 1);
178
        this.hasHalf.set(isHalf);
1✔
179
        const _value = isHalf ? Number(this.currentValue() - 0.5) : this.currentValue();
180
        this.thyItemHoverChange.emit(_value);
181
    }
182

183
    itemClick(isHalf: boolean, index: number) {
184
        if (this.thyDisabled) {
UNCOV
185
            return;
×
186
        }
187
        this.currentValue.set(index + 1);
188
        const _value = isHalf ? index + 1 - 0.5 : index + 1;
189
        if (this.thyValue() === _value) {
190
            if (this.thyAllowClear()) {
191
                this.thyValue.set(0);
192
                this.onChangeCallback(this.thyValue());
193
                this.onTouchedCallback();
194
            }
195
        } else {
196
            this.thyValue.set(_value);
197
            this.onChangeCallback(this.thyValue());
198
            this.onTouchedCallback();
199
        }
200
    }
201

202
    onRateLeave(event: Event): void {
203
        event.stopPropagation();
204
        this.hasHalf.set(!Number.isInteger(this.thyValue()));
205
        this.currentValue.set(Math.ceil(this.thyValue()));
206
    }
207

208
    updateRateArray(): number[] {
209
        return this.thyCount() > 0
210
            ? Array(this.thyCount())
211
                  .fill(0)
212
                  .map((_, i) => {
213
                      return i;
214
                  })
215
            : [];
216
    }
217

218
    updateItemStyle(): Record<string, boolean>[] {
219
        const rateStyle = 'thy-rate-star';
220
        return this.rateArray().map(i => {
221
            const value = i + 1;
222
            return {
223
                [`${rateStyle}--full`]: value < this.currentValue() || (value === this.currentValue() && !this.hasHalf()),
224
                [`${rateStyle}--half`]: this.hasHalf() && value === this.currentValue(),
225
                [`${rateStyle}--active`]: this.hasHalf() && value === this.currentValue(),
226
                [`${rateStyle}--zero`]: value > this.currentValue()
227
            };
228
        });
229
    }
230

231
    registerOnChange(fn: any): void {
232
        this.onChangeCallback = fn;
233
    }
234

235
    registerOnTouched(fn: any): void {
236
        this.onTouchedCallback = fn;
237
    }
238

239
    trackByFn(index: number, item: any) {
240
        return item || index;
241
    }
242
}
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