• 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

5.1
/src/date-range/date-range.component.ts
1
import { ChangeDetectorRef, Component, computed, forwardRef, inject, Signal, input, output } from '@angular/core';
2
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
3
import { ThyPopover } from 'ngx-tethys/popover';
4
import { DateRangeItemInfo } from './date-range.class';
5
import { OptionalDateRanges } from './optional-dates/optional-dates.component';
6

7
import { NgClass } from '@angular/common';
8
import { ThyAction } from 'ngx-tethys/action';
9
import { ThyDatePickerConfigService, ThyDatePickerFormatPipe } from 'ngx-tethys/date-picker';
10
import { injectLocale, ThyDateRangeLocale } from 'ngx-tethys/i18n';
11
import { ThyIcon } from 'ngx-tethys/icon';
12
import {
13
    addDays,
14
    addMonths,
1✔
15
    addYears,
1✔
16
    coerceBooleanProperty,
UNCOV
17
    endOfDay,
×
18
    endOfISOWeek,
19
    endOfMonth,
20
    getUnixTime,
21
    isSameDay,
22
    startOfDay,
23
    startOfISOWeek,
24
    startOfMonth,
25
    TinyDate
1✔
26
} from 'ngx-tethys/util';
UNCOV
27

×
UNCOV
28
const allDayTimestamp = 24 * 60 * 60;
×
UNCOV
29

×
UNCOV
30
const INPUT_CONTROL_VALUE_ACCESSOR: any = {
×
UNCOV
31
    provide: NG_VALUE_ACCESSOR,
×
UNCOV
32
    useExisting: forwardRef(() => ThyDateRange),
×
UNCOV
33
    multi: true
×
UNCOV
34
};
×
UNCOV
35

×
UNCOV
36
/**
×
UNCOV
37
 * 预设时间段及自定义时间段选择组件
×
UNCOV
38
 * @name thy-date-range
×
UNCOV
39
 * @order 10
×
UNCOV
40
 */
×
UNCOV
41
@Component({
×
UNCOV
42
    selector: 'thy-date-range',
×
43
    templateUrl: './date-range.component.html',
UNCOV
44
    providers: [INPUT_CONTROL_VALUE_ACCESSOR],
×
UNCOV
45
    imports: [ThyAction, ThyIcon, NgClass, ThyDatePickerFormatPipe]
×
46
})
47
export class ThyDateRange implements ControlValueAccessor {
48
    private thyPopover = inject(ThyPopover);
49
    private cdr = inject(ChangeDetectorRef);
50
    private locale: Signal<ThyDateRangeLocale> = injectLocale('dateRange');
51
    private datePickerConfigService = inject(ThyDatePickerConfigService);
52

53
    /**
54
     * 自定义可选值列表项
55
     * @type DateRangeItemInfo[]
56
     */
57
    readonly thyOptionalDateRanges = input<DateRangeItemInfo[]>();
58

59
    /**
60
     * 隐藏下拉选择时间段
61
     * @default false
62
     */
63
    readonly thyHiddenMenu = input(false, { transform: coerceBooleanProperty });
64

65
    /**
66
     * 禁用左右切换时间段
UNCOV
67
     * @default false
×
UNCOV
68
     */
×
UNCOV
69
    readonly thyDisabledSwitch = input(false, { transform: coerceBooleanProperty });
×
70

UNCOV
71
    /**
×
72
     * 自定义日期选择的展示文字
UNCOV
73
     * @default 自定义
×
UNCOV
74
     */
×
75
    readonly thyCustomTextValue = input(this.locale().custom);
76

UNCOV
77
    /**
×
UNCOV
78
     * 自定义日期选择中可选择的最小时间
×
79
     * @type Date | number
UNCOV
80
     */
×
UNCOV
81
    readonly thyMinDate = input<Date | number>(undefined);
×
UNCOV
82

×
83
    /**
UNCOV
84
     * 自定义日期选择中可选择的最大时间
×
UNCOV
85
     * @type Date | number
×
86
     */
87
    readonly thyMaxDate = input<Date | number>(undefined);
UNCOV
88

×
89
    /**
90
     * 选中的时间段的展示形式,
UNCOV
91
     * <br/> `custom`形式:`2023-07-01 ~ 2023-07-31`;
×
92
     * <br/> `exception`形式:`2023-07-01`,具体展示还与`thyPickerFormat`有关。
93
     */
UNCOV
94
    readonly thyCustomKey = input<'custom' | 'exception'>('custom');
×
95

96
    /**
97
     * 自定义日期展示格式,比如`yyyy年MM月`,只有当`thyCustomKey`值设为`exception`时才会生效
98
     */
99
    readonly thyPickerFormat = input<string>(undefined);
UNCOV
100

×
UNCOV
101
    /**
×
UNCOV
102
     * 自定义日期禁用日期
×
UNCOV
103
     */
×
UNCOV
104
    readonly thyDisabledDate = input<(d: Date) => boolean>(undefined);
×
UNCOV
105

×
UNCOV
106
    /**
×
107
     * 区间分隔符,不传值默认为 "~"
108
     */
109
    readonly thySeparator = input<string>(this.datePickerConfigService.config?.separator);
110

111
    separator = computed(() => {
112
        return ` ${this.thySeparator()?.trim()} `;
UNCOV
113
    });
×
114

115
    /**
116
     * 自定义日期选择日期回调
117
     * @type EventEmitter<Date[]>
118
     */
119
    readonly thyOnCalendarChange = output<Date[]>();
UNCOV
120

×
UNCOV
121
    public selectedDate?: DateRangeItemInfo;
×
UNCOV
122

×
123
    private defaultOptionalDateRanges: DateRangeItemInfo[] = [
124
        {
125
            key: 'week',
126
            text: this.locale().currentWeek,
127
            begin: getUnixTime(startOfISOWeek(new TinyDate().getTime())),
128
            end: getUnixTime(endOfISOWeek(new TinyDate().getTime())),
UNCOV
129
            timestamp: {
×
UNCOV
130
                interval: 7,
×
131
                unit: 'day'
132
            }
×
133
        },
134
        {
135
            key: 'month',
136
            text: this.locale().currentMonth,
137
            begin: getUnixTime(startOfMonth(new TinyDate().getTime())),
138
            end: getUnixTime(endOfMonth(new TinyDate().getTime())),
UNCOV
139
            timestamp: {
×
UNCOV
140
                interval: 1,
×
UNCOV
141
                unit: 'month'
×
142
            }
143
        }
144
    ];
145

146
    public optionalDateRanges = computed<DateRangeItemInfo[]>(() => {
147
        if (this.thyOptionalDateRanges()?.length > 0) {
UNCOV
148
            return this.thyOptionalDateRanges();
×
149
        }
150
        return this.defaultOptionalDateRanges;
151
    });
152

153
    public selectedDateRange: {
154
        begin: number;
155
        end: number;
156
    };
UNCOV
157

×
UNCOV
158
    public onModelChange: Function = () => {};
×
UNCOV
159

×
160
    public onModelTouched: Function = () => {};
161

162
    writeValue(value: any): void {
163
        if (value) {
164
            this.selectedDate = value;
165
        } else if (this.optionalDateRanges().length > 0) {
UNCOV
166
            this.selectedDate = this.optionalDateRanges()[0];
×
167
            this.onModelChange(this.selectedDate);
168
        }
169
        this._setSelectedDateRange();
170
        this.cdr.detectChanges();
171
    }
172

173
    registerOnChange(fn: any): void {
174
        this.onModelChange = fn;
UNCOV
175
    }
×
UNCOV
176

×
UNCOV
177
    registerOnTouched(fn: any): void {
×
178
        this.onModelTouched = fn;
179
    }
UNCOV
180

×
181
    private _setSelectedDateRange() {
182
        this.selectedDateRange = {
UNCOV
183
            begin: this.selectedDate.begin,
×
184
            end: this.selectedDate.end
185
        };
UNCOV
186
    }
×
UNCOV
187

×
188
    private _calculateNewTime(type: string) {
UNCOV
189
        if (this.selectedDate.timestamp) {
×
190
            const beginDate = new TinyDate(this.selectedDate.begin * 1000)?.nativeDate;
191
            const endDate = new TinyDate(this.selectedDate.end * 1000)?.nativeDate;
192
            const interval = this.selectedDate.timestamp.interval;
193

194
            if (this.selectedDate.timestamp.unit === 'day') {
195
                if (type === 'previous') {
196
                    return {
197
                        begin: getUnixTime(addDays(beginDate, -1 * interval)),
198
                        end: getUnixTime(addDays(endDate, -1 * interval)),
199
                        key: this.thyCustomKey()
200
                    };
201
                } else {
202
                    return {
203
                        begin: getUnixTime(addDays(beginDate, 1 * interval)),
204
                        end: getUnixTime(addDays(endDate, 1 * interval)),
205
                        key: this.thyCustomKey()
UNCOV
206
                    };
×
UNCOV
207
                }
×
208
            } else if (this.selectedDate.timestamp.unit === 'month') {
209
                if (type === 'previous') {
UNCOV
210
                    return {
×
211
                        begin: getUnixTime(addMonths(beginDate, -1 * interval)),
212
                        end: getUnixTime(endOfDay(addDays(beginDate, -1))),
213
                        key: this.thyCustomKey()
214
                    };
215
                } else {
1✔
216
                    const endIsEndDayOfMonth = isSameDay(endDate, endOfMonth(endDate));
217
                    return {
218
                        begin: getUnixTime(startOfDay(addDays(endDate, 1))),
219
                        end: endIsEndDayOfMonth
220
                            ? getUnixTime(endOfMonth(addMonths(endDate, 1 * interval)))
221
                            : getUnixTime(addMonths(endDate, 1 * interval)),
222
                        key: this.thyCustomKey()
223
                    };
224
                }
225
            } else if (this.selectedDate.timestamp.unit === 'year') {
226
                if (type === 'previous') {
227
                    return {
228
                        begin: getUnixTime(addYears(beginDate, -1 * interval)),
229
                        end: getUnixTime(addYears(endDate, -1 * interval)),
1✔
230
                        key: this.thyCustomKey()
231
                    };
232
                } else {
233
                    return {
234
                        begin: getUnixTime(addYears(beginDate, 1 * interval)),
235
                        end: getUnixTime(addYears(endDate, 1 * interval)),
236
                        key: this.thyCustomKey()
237
                    };
238
                }
239
            }
240
        } else {
241
            const interval: number = this.selectedDate.end - this.selectedDate.begin + allDayTimestamp;
242
            if (type === 'previous') {
243
                return {
244
                    begin: this.selectedDate.begin - interval,
245
                    end: this.selectedDate.end - interval,
246
                    key: this.thyCustomKey()
247
                };
248
            } else {
249
                return {
250
                    begin: this.selectedDate.begin + interval,
251
                    end: this.selectedDate.end + interval,
252
                    key: this.thyCustomKey()
253
                };
254
            }
255
        }
256
    }
257

258
    private _setPreviousOrNextDate(type: string) {
259
        this.selectedDate = Object.assign({}, this.selectedDate, this._calculateNewTime(type));
260
        this._setSelectedDateRange();
261
        this.onModelChange(this.selectedDate);
262
    }
263

264
    public previous() {
265
        this._setPreviousOrNextDate('previous');
266
    }
267

268
    public next() {
269
        this._setPreviousOrNextDate('next');
270
    }
271

272
    public openOptionalDateRangesMenu(event: Event) {
273
        if (this.thyHiddenMenu()) {
274
            return;
275
        }
276
        this.thyPopover.open(OptionalDateRanges, {
277
            origin: event.currentTarget as HTMLElement,
278
            hasBackdrop: true,
279
            backdropClass: 'thy-overlay-transparent-backdrop',
280
            offset: 0,
281
            manualClosure: true,
282
            originActiveClass: 'thy-date-range-text-active',
283
            initialState: {
284
                hiddenMenu: this.thyHiddenMenu(),
285
                optionalDateRanges: this.optionalDateRanges(),
286
                selectedDate: this.selectedDate,
287
                minDate: this.thyMinDate(),
288
                maxDate: this.thyMaxDate(),
289
                customValue: this.thyCustomTextValue(),
290
                customKey: this.thyCustomKey(),
291
                disabledDate: this.thyDisabledDate(),
292
                selectedDateRange: (dateRange: DateRangeItemInfo) => {
293
                    this.onModelChange(dateRange);
294
                    this.selectedDate = dateRange;
295
                },
296
                calendarChange: (date: Date[]) => {
297
                    this.thyOnCalendarChange.emit(date);
298
                }
299
            }
300
        });
301
    }
302
}
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