• 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

0.6
/src/date-picker/lib/popups/date-popup.component.ts
1
import {
2
    endOfDay,
3
    FunctionProp,
4
    helpers,
5
    isFunction,
6
    isUndefinedOrNull,
7
    sortRangeValue,
8
    startOfDay,
9
    TinyDate,
10
    TinyDateCompareGrain
11
} from 'ngx-tethys/util';
12

13
import {
14
    ChangeDetectionStrategy,
15
    ChangeDetectorRef,
16
    Component,
17
    EventEmitter,
18
    inject,
1✔
19
    Input,
UNCOV
20
    OnChanges,
×
UNCOV
21
    OnInit,
×
UNCOV
22
    Output,
×
UNCOV
23
    Signal,
×
UNCOV
24
    SimpleChanges,
×
UNCOV
25
    TemplateRef
×
UNCOV
26
} from '@angular/core';
×
UNCOV
27

×
UNCOV
28
import { NgTemplateOutlet } from '@angular/common';
×
UNCOV
29
import { FormsModule } from '@angular/forms';
×
UNCOV
30
import { ThyButtonIcon } from 'ngx-tethys/button';
×
UNCOV
31
import { injectLocale, ThyDatePickerLocale } from 'ngx-tethys/i18n';
×
UNCOV
32
import { ThyNav, ThyNavItemDirective } from 'ngx-tethys/nav';
×
UNCOV
33
import { ThyDatePickerConfigService } from '../../date-picker.service';
×
UNCOV
34
import { CompatibleValue, DatePickerFlexibleTab, RangeAdvancedValue, RangePartType } from '../../inner-types';
×
UNCOV
35
import { dateAddAmount, getShortcutValue, hasValue, makeValue, setValueByTimestampPrecision, transformDateValue } from '../../picker.util';
×
36
import {
37
    CompatibleDate,
UNCOV
38
    CompatiblePresets,
×
39
    DisabledDateFn,
40
    SupportTimeOptions,
UNCOV
41
    ThyDateChangeEvent,
×
UNCOV
42
    ThyDateGranularity,
×
43
    ThyPanelMode,
44
    ThyShortcutPosition,
UNCOV
45
    ThyShortcutPreset,
×
UNCOV
46
    ThyShortcutValue
×
UNCOV
47
} from '../../standard-types';
×
48
import { CalendarFooter } from '../calendar/calendar-footer.component';
×
49
import { DateCarousel } from '../date-carousel/date-carousel.component';
UNCOV
50
import { InnerPopup } from './inner-popup.component';
×
51

×
52
/**
×
53
 * @private
UNCOV
54
 */
×
UNCOV
55
@Component({
×
UNCOV
56
    changeDetection: ChangeDetectionStrategy.OnPush,
×
UNCOV
57
    // eslint-disable-next-line @angular-eslint/component-selector
×
58
    selector: 'date-popup',
59
    exportAs: 'datePopup',
60
    templateUrl: './date-popup.component.html',
UNCOV
61
    imports: [ThyNav, ThyNavItemDirective, ThyButtonIcon, DateCarousel, FormsModule, NgTemplateOutlet, InnerPopup, CalendarFooter]
×
UNCOV
62
})
×
UNCOV
63
export class DatePopup implements OnChanges, OnInit {
×
64
    private cdr = inject(ChangeDetectorRef);
65
    private datePickerConfigService = inject(ThyDatePickerConfigService);
UNCOV
66
    locale: Signal<ThyDatePickerLocale> = injectLocale('datePicker');
×
67

68
    @Input() isRange: boolean;
UNCOV
69
    @Input() showWeek: boolean;
×
UNCOV
70

×
71
    @Input() format: string;
UNCOV
72
    @Input() disabledDate: DisabledDateFn;
×
UNCOV
73
    @Input() minDate: Date | number;
×
74
    @Input() maxDate: Date | number;
75
    @Input() showToday: boolean;
76

UNCOV
77
    /**
×
UNCOV
78
     * 是否支持设置时间(时、分)
×
79
     */
×
80
    @Input() showTime: SupportTimeOptions | boolean;
81

UNCOV
82
    /**
×
UNCOV
83
     * 是否展示时间(时、分)
×
UNCOV
84
     */
×
85
    @Input() mustShowTime: boolean;
UNCOV
86

×
UNCOV
87
    @Input() dateRender: FunctionProp<TemplateRef<Date> | string>;
×
UNCOV
88
    @Input() className: string;
×
UNCOV
89
    @Input() panelMode: ThyPanelMode | ThyPanelMode[];
×
UNCOV
90
    @Input() value: CompatibleValue;
×
UNCOV
91
    @Input() defaultPickerValue: CompatibleDate | number;
×
UNCOV
92

×
UNCOV
93
    @Input() showShortcut: boolean;
×
UNCOV
94

×
UNCOV
95
    @Input() shortcutPresets: CompatiblePresets;
×
UNCOV
96

×
UNCOV
97
    @Input() shortcutPosition: ThyShortcutPosition;
×
UNCOV
98

×
UNCOV
99
    @Input() flexible: boolean;
×
100

101
    @Input() flexibleDateGranularity: ThyDateGranularity;
UNCOV
102

×
103
    @Input() timestampPrecision: 'seconds' | 'milliseconds';
104

105
    @Input() timeZone: string;
106

UNCOV
107
    @Output() readonly panelModeChange = new EventEmitter<ThyPanelMode | ThyPanelMode[]>();
×
UNCOV
108
    @Output() readonly calendarChange = new EventEmitter<CompatibleValue>();
×
UNCOV
109
    @Output() readonly valueChange = new EventEmitter<CompatibleValue | RangeAdvancedValue>();
×
UNCOV
110
    @Output() readonly resultOk = new EventEmitter<void>(); // Emitted when done with date selecting
×
UNCOV
111
    @Output() readonly showTimePickerChange = new EventEmitter<boolean>();
×
112
    @Output() readonly dateValueChange = new EventEmitter<ThyDateChangeEvent>();
113

UNCOV
114
    prefixCls = 'thy-calendar';
×
115
    showTimePicker = false;
116
    timeOptions: SupportTimeOptions | SupportTimeOptions[] | null;
117
    activeDate: TinyDate | TinyDate[];
118
    selectedValue: TinyDate[] = []; // Range ONLY
119
    hoverValue: TinyDate[] = []; // Range ONLY
120

121
    advancedSelectedValue: RangeAdvancedValue; // advanced ONLY
UNCOV
122

×
UNCOV
123
    flexibleActiveTab: DatePickerFlexibleTab = 'advanced';
×
UNCOV
124

×
UNCOV
125
    get hasTimePicker(): boolean {
×
126
        return !!this.showTime;
UNCOV
127
    }
×
UNCOV
128
    private partTypeMap: { [key: string]: number } = { left: 0, right: 1 };
×
UNCOV
129

×
130
    [property: string]: any;
UNCOV
131

×
132
    endPanelMode: ThyPanelMode | ThyPanelMode[];
133

UNCOV
134
    innerShortcutPresets: ThyShortcutPreset[];
×
135

UNCOV
136
    disableTimeConfirm = false;
×
137

138
    setProperty<T extends keyof DatePopup>(key: T, value: this[T]): void {
UNCOV
139
        this[key] = value;
×
140
        this.cdr.markForCheck();
×
141
    }
×
142

143
    ngOnInit(): void {
144
        this.initShortcutPresets();
×
145
        this.initPanelMode();
146
        if (this.flexible && this.flexibleDateGranularity === 'day') {
147
            this.flexibleActiveTab = 'custom';
UNCOV
148
        }
×
UNCOV
149
        if (this.defaultPickerValue && !hasValue(this.value)) {
×
150
            const { value } = transformDateValue(this.defaultPickerValue);
151
            this.value = makeValue(value, this.isRange, this.timeZone);
UNCOV
152
        }
×
153
        this.updateActiveDate();
154
        this.initDisabledDate();
155
        if (this.isRange && this.flexible && this.value) {
156
            this.advancedSelectedValue = { begin: this.value[0], end: this.value[1], dateGranularity: this.flexibleDateGranularity };
157
        }
158
    }
159

UNCOV
160
    ngOnChanges(changes: SimpleChanges): void {
×
UNCOV
161
        if (changes.panelMode) {
×
UNCOV
162
            if (helpers.isArray(this.panelMode)) {
×
163
                this.endPanelMode = [...this.panelMode];
UNCOV
164
            } else {
×
UNCOV
165
                this.endPanelMode = this.panelMode;
×
UNCOV
166
            }
×
167
        }
UNCOV
168
        if (changes.defaultPickerValue) {
×
UNCOV
169
            this.updateActiveDate();
×
170
        }
UNCOV
171
        if (changes.value && changes.value.currentValue) {
×
UNCOV
172
            this.updateActiveDate();
×
UNCOV
173
        }
×
UNCOV
174
    }
×
175

UNCOV
176
    initShortcutPresets(): void {
×
UNCOV
177
        const { shortcutRangesPresets, shortcutDatePresets, showShortcut } = this.datePickerConfigService;
×
178

UNCOV
179
        this.showShortcut =
×
UNCOV
180
            ['date', 'date,date'].includes(this.panelMode.toString()) && isUndefinedOrNull(this.showShortcut)
×
181
                ? showShortcut
UNCOV
182
                : this.showShortcut;
×
183

184
        if (this.showShortcut) {
185
            if (!this.shortcutPresets) {
UNCOV
186
                this.shortcutPresets = this.isRange ? shortcutRangesPresets : shortcutDatePresets;
×
UNCOV
187
            }
×
188

189
            this.innerShortcutPresets = isFunction(this.shortcutPresets) ? this.shortcutPresets() : this.shortcutPresets;
UNCOV
190
            if (this.innerShortcutPresets.length) {
×
UNCOV
191
                const minDate: TinyDate = this.getMinTinyDate();
×
UNCOV
192
                const maxDate: TinyDate = this.getMaxTinyDate();
×
193

194
                const minTime = minDate ? minDate.getTime() : null;
UNCOV
195
                const maxTime = maxDate ? maxDate.getTime() : null;
×
UNCOV
196

×
197
                if (this.isRange) {
198
                    this.innerShortcutPresets.forEach((preset: ThyShortcutPreset) => {
UNCOV
199
                        const begin: number | Date = getShortcutValue(preset.value[0]);
×
200
                        const beginTime: number = new TinyDate(startOfDay(begin), this.timeZone).getTime();
201

×
202
                        const end: number | Date = getShortcutValue(preset.value[1]);
×
203
                        const endTime: number = new TinyDate(endOfDay(end), this.timeZone).getTime();
×
204

205
                        if ((minDate && endTime < minTime) || (maxDate && beginTime > maxTime)) {
206
                            preset.disabled = true;
×
207
                        } else {
208
                            preset.disabled = false;
209
                        }
210
                    });
UNCOV
211
                } else {
×
UNCOV
212
                    this.innerShortcutPresets.forEach((preset: ThyShortcutPreset) => {
×
213
                        const singleValue: number | Date = getShortcutValue(preset.value as ThyShortcutValue);
214
                        const singleTime: number = new TinyDate(singleValue, this.timeZone).getTime();
UNCOV
215

×
216
                        if ((minDate && singleTime < minTime) || (maxDate && singleTime > maxTime)) {
UNCOV
217
                            preset.disabled = true;
×
218
                        } else {
219
                            preset.disabled = false;
UNCOV
220
                        }
×
UNCOV
221
                    });
×
UNCOV
222
                }
×
223
            }
224
        }
UNCOV
225
    }
×
226

227
    updateActiveDate() {
228
        this.clearHoverValue();
UNCOV
229
        if (!this.value) {
×
230
            const { value } = transformDateValue(this.defaultPickerValue);
231
            this.value = makeValue(value, this.isRange, this.timeZone);
232
        }
UNCOV
233
        if (this.isRange) {
×
234
            if (!this.flexible || this.flexibleDateGranularity === 'day') {
235
                this.selectedValue = this.value as TinyDate[];
236
            }
UNCOV
237
            this.activeDate = this.normalizeRangeValue(this.value as TinyDate[], this.getPanelMode(this.endPanelMode) as ThyPanelMode);
×
238
        } else {
239
            this.activeDate = this.value as TinyDate;
UNCOV
240
        }
×
UNCOV
241
        this.isDisableTimeConfirm();
×
UNCOV
242
    }
×
243

244
    initPanelMode() {
245
        if (!this.endPanelMode) {
×
246
            if (helpers.isArray(this.panelMode)) {
UNCOV
247
                this.endPanelMode = [...this.panelMode];
×
248
            } else {
249
                this.endPanelMode = this.panelMode;
UNCOV
250
            }
×
251
        } else {
UNCOV
252
            if (helpers.isArray(this.endPanelMode)) {
×
UNCOV
253
                this.panelMode = [...this.endPanelMode];
×
254
            } else {
255
                this.panelMode = this.endPanelMode;
UNCOV
256
            }
×
257
        }
UNCOV
258
    }
×
UNCOV
259

×
UNCOV
260
    initDisabledDate(): void {
×
261
        let minDate: TinyDate;
UNCOV
262
        let maxDate: TinyDate;
×
UNCOV
263
        let disabledDateFn: DisabledDateFn;
×
UNCOV
264
        if (this.minDate) {
×
265
            const { value } = transformDateValue(this.minDate);
UNCOV
266
            minDate = new TinyDate(value as Date, this.timeZone);
×
267
        }
UNCOV
268
        if (this.maxDate) {
×
UNCOV
269
            const { value } = transformDateValue(this.maxDate);
×
UNCOV
270
            maxDate = new TinyDate(value as Date, this.timeZone);
×
UNCOV
271
        }
×
UNCOV
272
        if (this.disabledDate) {
×
UNCOV
273
            disabledDateFn = this.disabledDate;
×
UNCOV
274
        }
×
UNCOV
275
        this.disabledDate = d => {
×
276
            let expression = false;
277
            if (minDate) {
278
                expression = d < minDate.startOfDay().nativeDate;
UNCOV
279
            }
×
UNCOV
280
            if (maxDate && !expression) {
×
UNCOV
281
                expression = d > maxDate.endOfDay().nativeDate;
×
282
            }
283
            if (disabledDateFn && typeof disabledDateFn === 'function' && !expression) {
284
                expression = disabledDateFn(d);
UNCOV
285
            }
×
UNCOV
286
            return expression;
×
UNCOV
287
        };
×
288
    }
UNCOV
289

×
UNCOV
290
    onShowTimePickerChange(show: boolean): void {
×
291
        this.showTimePicker = show;
UNCOV
292
        this.showTimePickerChange.emit(show);
×
293
    }
×
294

UNCOV
295
    onClickOk(): void {
×
296
        this.setValue(this.value);
×
297
        this.valueChange.emit(this.value);
298
        this.resultOk.emit();
UNCOV
299
    }
×
300

301
    onClickRemove(): void {
302
        this.value = this.isRange ? [] : null;
UNCOV
303
        this.valueChange.emit(this.value);
×
UNCOV
304
    }
×
305

UNCOV
306
    onDayHover(value: TinyDate): void {
×
UNCOV
307
        if (this.isRange && this.selectedValue[0] && !this.selectedValue[1]) {
×
UNCOV
308
            // When right value is selected, don't do hover
×
UNCOV
309
            const base = this.selectedValue[0]; // Use the left of selected value as the base to decide later hoverValue
×
UNCOV
310
            if (base.isBeforeDay(value)) {
×
UNCOV
311
                this.hoverValue = [base, value];
×
312
            } else {
313
                this.hoverValue = [value, base];
UNCOV
314
            }
×
315
        }
316
    }
317

UNCOV
318
    onPanelModeChange(mode: ThyPanelMode, partType?: RangePartType): void {
×
UNCOV
319
        if (this.isRange) {
×
UNCOV
320
            (this.panelMode as ThyPanelMode[])[this.getPartTypeIndex(partType)] = mode;
×
UNCOV
321
        } else {
×
UNCOV
322
            this.panelMode = mode;
×
323
        }
UNCOV
324
        this.panelModeChange.emit(this.panelMode);
×
325
    }
326

UNCOV
327
    onHeaderChange(value: TinyDate, partType?: RangePartType): void {
×
328
        if (this.isRange) {
329
            this.activeDate[this.getPartTypeIndex(partType)] = value;
330
            this.activeDate = this.normalizeRangeValue(
UNCOV
331
                this.activeDate as TinyDate[],
×
UNCOV
332
                this.getPanelMode(this.endPanelMode, partType) as ThyPanelMode
×
333
            );
334
        } else {
UNCOV
335
            this.activeDate = value;
×
336
        }
337
    }
338

UNCOV
339
    onSelectTime(value: TinyDate, partType?: RangePartType): void {
×
UNCOV
340
        if (this.isRange) {
×
UNCOV
341
            // TODO:range picker set time
×
342
        } else {
343
            this.setValue(new TinyDate(value.nativeDate, this.timeZone));
UNCOV
344
        }
×
345
    }
346

347
    selectTab(active: DatePickerFlexibleTab) {
UNCOV
348
        this.flexibleActiveTab = active;
×
UNCOV
349
    }
×
350

351
    clearFlexibleValue() {
UNCOV
352
        this.flexibleDateGranularity = null;
×
353
        if (this.flexibleActiveTab === 'advanced') {
354
            this.advancedSelectedValue = {};
355
        } else {
×
UNCOV
356
            this.selectedValue = [];
×
357
        }
358
        this.valueChange.emit({ begin: null, end: null, dateGranularity: this.flexibleDateGranularity });
UNCOV
359
    }
×
360

361
    changeValueFromAdvancedSelect(value: RangeAdvancedValue) {
UNCOV
362
        this.valueChange.emit(value);
×
363
        // clear custom date when select a advanced date
364
        this.selectedValue = [];
UNCOV
365
        this.dateValueChange.emit({ value: [value.begin, value.end] });
×
366
    }
367

UNCOV
368
    changeValueFromSelect(value: TinyDate, partType?: RangePartType): void {
×
UNCOV
369
        if (this.isRange) {
×
UNCOV
370
            // clear advanced date when select a custom date
×
UNCOV
371
            this.advancedSelectedValue = {};
×
372

373
            const [left, right] = this.selectedValue as TinyDate[];
UNCOV
374

×
UNCOV
375
            if ((!left && !right) || (left && right)) {
×
376
                // If totally full or empty, clean up && re-assign left first
377
                this.hoverValue = this.selectedValue = [value];
UNCOV
378
                this.selectedValue = [this.selectedValue[0].startOfDay()];
×
379
                this.calendarChange.emit([this.selectedValue[0].clone()]);
380
            } else if (left && !right) {
×
UNCOV
381
                // If one of them is empty, assign the other one and sort, then set the final values
×
382
                this.clearHoverValue(); // Clean up
383
                this.setRangeValue('right', value);
384
                this.selectedValue = sortRangeValue(this.selectedValue); // Sort
385
                this.selectedValue = this.getSelectedRangeValueByMode(this.selectedValue);
386
                this.activeDate = this.normalizeRangeValue(
387
                    this.selectedValue,
UNCOV
388
                    this.getPanelMode(this.endPanelMode, partType) as ThyPanelMode
×
UNCOV
389
                );
×
UNCOV
390
                this.setValue(this.cloneRangeDate(this.selectedValue));
×
UNCOV
391
                this.calendarChange.emit(this.cloneRangeDate(this.selectedValue));
×
UNCOV
392
                this.dateValueChange.emit({ value: this.cloneRangeDate(this.selectedValue) });
×
UNCOV
393
            }
×
394
        } else {
UNCOV
395
            const updatedValue = this.updateHourMinute(value);
×
396
            this.setValue(updatedValue);
397
            this.dateValueChange.emit({ value: updatedValue });
UNCOV
398
        }
×
UNCOV
399
    }
×
400

401
    private getSelectedRangeValueByMode(value: TinyDate[]): TinyDate[] {
UNCOV
402
        const panelMode = this.getPanelMode(this.endPanelMode);
×
403
        if (panelMode === 'year') {
404
            return [value[0].startOfYear(), value[1].endOfYear()];
UNCOV
405
        } else if (panelMode === 'quarter') {
×
UNCOV
406
            return [value[0].startOfQuarter(), value[1].endOfQuarter()];
×
407
        } else if (panelMode === 'month') {
UNCOV
408
            return [value[0].startOfMonth(), value[1].endOfMonth()];
×
UNCOV
409
        } else if (panelMode === 'week') {
×
UNCOV
410
            return [value[0].startOfISOWeek(), value[1].endOfISOWeek()];
×
UNCOV
411
        } else {
×
UNCOV
412
            return [value[0].startOfDay(), value[1].endOfDay()];
×
413
        }
414
    }
UNCOV
415

×
416
    private updateHourMinute(value: TinyDate): TinyDate {
417
        if (!this.value) {
418
            return value;
UNCOV
419
        }
×
UNCOV
420
        const originDate = this.value as TinyDate;
×
UNCOV
421
        const dateTime = [value.getHours(), value.getMinutes(), value.getSeconds()];
×
UNCOV
422
        const originDateTime = [originDate.getHours(), originDate.getMinutes(), originDate.getSeconds()];
×
UNCOV
423

×
UNCOV
424
        const isEqualTime = dateTime.toString() === originDateTime.toString();
×
UNCOV
425
        if (isEqualTime) {
×
UNCOV
426
            return value;
×
UNCOV
427
        } else {
×
UNCOV
428
            return value.setHms(originDateTime[0], originDateTime[1], originDateTime[2]);
×
429
        }
×
430
    }
UNCOV
431

×
UNCOV
432
    enablePrevNext(direction: 'prev' | 'next', partType?: RangePartType): boolean {
×
433
        if (this.isRange && this.panelMode === this.endPanelMode) {
UNCOV
434
            const [start, end] = this.activeDate as TinyDate[];
×
UNCOV
435
            const showMiddle = !start.addMonths(1).isSame(end, 'month'); // One month diff then don't show middle prev/next
×
436
            if ((partType === 'left' && direction === 'next') || (partType === 'right' && direction === 'prev')) {
UNCOV
437
                return showMiddle;
×
UNCOV
438
            }
×
439
            return true;
UNCOV
440
        } else {
×
441
            return true;
442
        }
UNCOV
443
    }
×
UNCOV
444

×
445
    getPanelMode(panelMode: ThyPanelMode | ThyPanelMode[], partType?: RangePartType): ThyPanelMode {
×
446
        if (this.isRange) {
UNCOV
447
            return panelMode[this.getPartTypeIndex(partType)] as ThyPanelMode;
×
448
        } else {
449
            return panelMode as ThyPanelMode;
450
        }
UNCOV
451
    }
×
UNCOV
452

×
453
    getValueBySelector(partType?: RangePartType): TinyDate {
UNCOV
454
        if (this.isRange) {
×
UNCOV
455
            const valueShow = this.selectedValue; // Use the real time value that without decorations when timepicker is shown up
×
UNCOV
456
            return (valueShow as TinyDate[])[this.getPartTypeIndex(partType)];
×
457
        } else {
458
            return this.value as TinyDate;
UNCOV
459
        }
×
UNCOV
460
    }
×
UNCOV
461

×
UNCOV
462
    getActiveDate(partType?: RangePartType): TinyDate {
×
UNCOV
463
        if (this.isRange) {
×
464
            return this.activeDate[this.getPartTypeIndex(partType)];
465
        } else {
466
            return this.activeDate as TinyDate;
UNCOV
467
        }
×
468
    }
469

470
    getPartTypeIndex(partType: RangePartType = 'left'): number {
UNCOV
471
        return this.partTypeMap[partType];
×
UNCOV
472
    }
×
UNCOV
473

×
UNCOV
474
    private getMinTinyDate() {
×
475
        return this.minDate ? new TinyDate(transformDateValue(this.minDate).value as Date, this.timeZone) : null;
UNCOV
476
    }
×
UNCOV
477

×
UNCOV
478
    private getMaxTinyDate() {
×
479
        return this.maxDate ? new TinyDate(transformDateValue(this.maxDate).value as Date, this.timeZone) : null;
×
480
    }
481

UNCOV
482
    private clearHoverValue(): void {
×
UNCOV
483
        this.hoverValue = [];
×
484
    }
485

486
    private setValue(value: CompatibleValue): void {
UNCOV
487
        this.value = value;
×
488
        if (this.isRange && this.flexible) {
489
            this.flexibleDateGranularity = 'day';
1✔
490
            this.valueChange.emit({ begin: value[0], end: value[1], dateGranularity: this.flexibleDateGranularity });
491
        } else {
492
            if (!this.showTime || !this.showTimePicker) {
493
                this.valueChange.emit(this.value);
494
            }
495
        }
496
        this.isDisableTimeConfirm();
497
    }
498

499
    private normalizeRangeValue(value: TinyDate[], mode: ThyPanelMode = 'month'): TinyDate[] {
500
        const headerModes: { [key in ThyPanelMode]?: ThyPanelMode } = {
501
            week: 'month',
502
            date: 'month',
503
            month: 'year',
504
            quarter: 'year',
505
            year: 'decade'
506
        };
507
        const headerMode = headerModes[mode];
508
        const [start, end] = value;
509
        const newStart = start || new TinyDate(undefined, this.timeZone);
510
        let newEnd = end;
511
        if (!newEnd || newStart.isSame(end, headerMode as TinyDateCompareGrain)) {
512
            newEnd = dateAddAmount(newStart, 1, headerMode);
513
        }
514
        return [newStart, newEnd];
515
    }
516

517
    private setRangeValue(partType: RangePartType, value: TinyDate): void {
518
        const ref = (this.selectedValue = this.cloneRangeDate(this.selectedValue as TinyDate[]));
519
        ref[this.getPartTypeIndex(partType)] = value;
1✔
520
    }
521

522
    private cloneRangeDate(value: TinyDate[]): TinyDate[] {
523
        return [value[0] && value[0].clone(), value[1] && value[1].clone()] as TinyDate[];
524
    }
525

526
    private isDisableTimeConfirm() {
527
        if (this.isRange || !this.showTime) {
528
            return;
529
        }
530

531
        const date: TinyDate = this.value ? (this.value as TinyDate) : new TinyDate(undefined, this.timeZone);
532
        const minDate: TinyDate = this.getMinTinyDate();
533
        const maxDate: TinyDate = this.getMaxTinyDate();
534

535
        if ((minDate && date.getTime() < minDate.getTime()) || (maxDate && date.getTime() > maxDate.getTime())) {
536
            this.disableTimeConfirm = true;
537
        } else {
538
            this.disableTimeConfirm = false;
539
        }
540
    }
541

542
    private getSelectedShortcutPreset(date: CompatibleValue): CompatibleValue {
543
        const minDate: TinyDate = this.getMinTinyDate();
544
        const maxDate: TinyDate = this.getMaxTinyDate();
545

546
        const minTime: number = (minDate && minDate.getTime()) || null;
547
        const maxTime: number = (maxDate && maxDate.getTime()) || null;
548

549
        if (helpers.isArray(date)) {
550
            const startDate: TinyDate = date[0];
551
            const endDate: TinyDate = date[1];
552

553
            const startTime: number = startDate.getTime();
554
            const endTime: number = endDate.getTime();
555

556
            if ((maxDate && startTime > maxTime) || (minDate && endTime < minTime)) {
557
                return [];
558
            }
559

560
            if (minDate && startTime < minTime && maxDate && endTime > maxTime) {
561
                return [minDate, maxDate];
562
            }
563

564
            if (minDate && startTime < minTime) {
565
                return [minDate, endDate];
566
            }
567

568
            if (maxDate && endTime > maxTime) {
569
                return [startDate, maxDate];
570
            }
571

572
            return date;
573
        } else {
574
            const singleTime: number = date.getTime();
575

576
            if ((minDate && singleTime < minTime) || (maxDate && singleTime > maxTime)) {
577
                return null;
578
            }
579

580
            return date;
581
        }
582
    }
583

584
    shortcutSetValue(shortcutPresets: ThyShortcutPreset) {
585
        if (shortcutPresets.disabled) {
586
            return;
587
        }
588

589
        const { value } = shortcutPresets;
590
        if (!value) {
591
            return;
592
        }
593

594
        let selectedPresetValue: CompatibleValue;
595
        if (helpers.isArray(value)) {
596
            const begin: number | Date = getShortcutValue(value[0]);
597
            const end: number | Date = getShortcutValue(value[1]);
598

599
            if (begin && end) {
600
                this.selectedValue = this.getSelectedShortcutPreset([
601
                    new TinyDate(begin, this.timeZone).startOfDay(),
602
                    new TinyDate(end, this.timeZone).endOfDay()
603
                ]) as TinyDate[];
604
                selectedPresetValue = this.cloneRangeDate(this.selectedValue);
605
            }
606
        } else {
607
            const originDate = this.value as TinyDate;
608
            const zonedTime = this.createInZoneTime(
609
                new TinyDate(getShortcutValue(value), this.timeZone),
610
                originDate?.getHours() ?? 0,
611
                originDate?.getMinutes() ?? 0,
612
                originDate?.getSeconds() ?? 0
613
            );
614
            const singleTinyDate: TinyDate = this.updateHourMinute(new TinyDate(zonedTime, this.timeZone));
615
            selectedPresetValue = this.getSelectedShortcutPreset(singleTinyDate) as TinyDate;
616
        }
617
        this.setValue(selectedPresetValue);
618
        const shortcutPresetsValue = setValueByTimestampPrecision(
619
            shortcutPresets?.value,
620
            this.isRange,
621
            this.timestampPrecision,
622
            this.timeZone
623
        ) as number;
624
        this.dateValueChange.emit({
625
            value: helpers.isArray(value) ? this.selectedValue : selectedPresetValue,
626
            triggerPreset: Object.assign({}, shortcutPresets, { value: shortcutPresetsValue })
627
        });
628
        if (!helpers.isArray(value) && this.showTime && this.showTimePicker) {
629
            this.updateActiveDate();
630
        }
631
    }
632

633
    private createInZoneTime(date: TinyDate, hours?: number, minutes?: number, seconds?: number): Date {
634
        return TinyDate.createDateInTimeZone(date.getYear(), date.getMonth(), date.getDate(), hours, minutes, seconds, this.timeZone);
635
    }
636
}
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