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

atinc / ngx-tethys / a94de615-1a97-46e8-b07e-aa98619b3649

13 Nov 2024 12:34PM UTC coverage: 90.366% (-0.03%) from 90.398%
a94de615-1a97-46e8-b07e-aa98619b3649

push

circleci

minlovehua
feat: support i18n

5517 of 6751 branches covered (81.72%)

Branch coverage included in aggregate %.

67 of 76 new or added lines in 19 files covered. (88.16%)

55 existing lines in 10 files now uncovered.

13225 of 13989 relevant lines covered (94.54%)

1000.62 hits per line

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

90.3
/src/date-picker/base-picker.component.ts
1
import { ThyClickDispatcher, ThyPlacement } from 'ngx-tethys/core';
2
import { elementMatchClosest, FunctionProp, TinyDate } from 'ngx-tethys/util';
3

4
import {
5
    Component,
6
    ElementRef,
7
    EventEmitter,
8
    Input,
9
    NgZone,
10
    OnChanges,
11
    OnInit,
12
    Output,
13
    PLATFORM_ID,
14
    TemplateRef,
1✔
15
    ViewChild,
16
    inject
153✔
17
} from '@angular/core';
153✔
18

153✔
19
import { AbstractPickerComponent } from './abstract-picker.component';
153✔
20
import { CompatibleValue, RangeAdvancedValue } from './inner-types';
153✔
21
import { CompatibleDate, ThyPanelMode } from './standard-types';
153✔
22
import { ThyPicker } from './picker.component';
153✔
23
import { hasTimeInStringDate, isValidStringDate, parseStringDate, transformDateValue } from './picker.util';
153✔
24
import { isPlatformBrowser } from '@angular/common';
153✔
25
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
153✔
26
import { coerceBooleanProperty } from '@angular/cdk/coercion';
153✔
27

153✔
28
/**
153✔
29
 * @private
30
 */
31
@Component({
153✔
32
    template: ``,
153✔
33
    standalone: true,
6✔
34
    host: {
35
        '[attr.tabindex]': `tabIndex`,
36
        '(focus)': 'onFocus($event)',
37
        '(blur)': 'onBlur($event)'
844✔
38
    }
39
})
40
export class BasePicker extends AbstractPickerComponent implements OnInit, OnChanges {
559✔
41
    protected element = inject(ElementRef);
42

43
    showWeek = false;
67!
44

45
    panelMode: ThyPanelMode | ThyPanelMode[];
46

153✔
47
    initialized: boolean;
153✔
48

153✔
49
    private innerPreviousDate: string;
153!
50

153✔
51
    @ViewChild('thyPicker', { static: true }) thyPicker: ThyPicker;
52

53
    @Input() thyDateRender: FunctionProp<TemplateRef<Date> | string>;
54

224✔
55
    @Input() set thyMode(value: ThyPanelMode) {
56
        this._panelMode = value ?? 'date';
57
        if (this.initialized) {
1✔
58
            this.setDefaultTimePickerState(this._panelMode);
1✔
59
        }
1✔
60
    }
61

62
    get thyMode() {
63
        return this._panelMode;
64
    }
65

66
    /**
47✔
67
     * 是否有幕布
47✔
68
     * @default true
47✔
69
     */
47✔
70
    @Input({ transform: coerceBooleanProperty }) thyHasBackdrop = true;
39✔
71

72
    /**
47✔
73
     * @type EventEmitter<ThyPanelMode | ThyPanelMode[]>
74
     */
75
    @Output() readonly thyOnPanelChange = new EventEmitter<ThyPanelMode | ThyPanelMode[]>();
14✔
76

6✔
77
    /**
6✔
78
     * @type EventEmitter<Date[]>
6✔
79
     */
6✔
80
    @Output() readonly thyOnCalendarChange = new EventEmitter<Date[]>();
81

8✔
82
    private _showTime: object | boolean;
8✔
83

8!
84
    /**
8✔
85
     * 增加时间选择功能
5✔
86
     * @default false
87
     */
88
    @Input() get thyShowTime(): object | boolean {
3✔
89
        return this._showTime;
90
    }
8✔
91
    set thyShowTime(value: object | boolean) {
8✔
92
        this._showTime = typeof value === 'object' ? value : coerceBooleanProperty(value);
8✔
93
    }
94

95
    /**
96
     * 是否展示时间(时、分)
159✔
97
     * @default false
159✔
98
     */
58✔
99
    @Input({ transform: coerceBooleanProperty }) thyMustShowTime = false;
100

101
    /**
101✔
102
     * 弹出位置
103
     * @type top | topLeft | topRight | bottom | bottomLeft | bottomRight | left | leftTop | leftBottom | right | rightTop | rightBottom
159✔
104
     */
159✔
105
    @Input() thyPlacement: ThyPlacement = 'bottomLeft';
121✔
106

107
    /**
108
     * @type EventEmitter<CompatibleDate | null>
109
     */
110
    @Output() readonly thyOnOk = new EventEmitter<CompatibleDate | null>();
121✔
111

112
    takeUntilDestroyed = takeUntilDestroyed();
121✔
113

114
    thyClickDispatcher = inject(ThyClickDispatcher);
115

116
    platformId = inject(PLATFORM_ID);
117

61✔
118
    ngZone = inject(NgZone);
10✔
119

120
    ngOnInit(): void {
121
        super.ngOnInit();
122
        this.setDefaultTimePickerState(this._panelMode);
123
        this.initialized = true;
10!
124

15✔
125
        if (isPlatformBrowser(this.platformId)) {
10✔
126
            this.thyClickDispatcher
127
                .clicked(0)
128
                .pipe(this.takeUntilDestroyed)
129
                .subscribe((event: Event) => {
66✔
130
                    if (
131
                        !this.element.nativeElement.contains(event.target) &&
132
                        !this.thyPicker?.overlayContainer?.nativeElement.contains(event.target as Node) &&
5!
133
                        this.realOpenState
×
134
                    ) {
×
UNCOV
135
                        this.ngZone.run(() => {
×
136
                            this.closeOverlay();
137
                            this.cdr.markForCheck();
UNCOV
138
                        });
×
139
                    }
140
                });
141
        }
142
    }
5!
143

5✔
144
    onValueChange(value: CompatibleValue | RangeAdvancedValue): void {
145
        this.thyPicker.entering = false;
UNCOV
146
        this.restoreTimePickerState(value as CompatibleValue);
×
147
        super.onValueChange(value);
148
        if (!this.flexible) {
149
            this.closeOverlay();
5✔
150
        }
151
        this.innerPreviousDate = this.thyPicker.getReadableValue(this.thyValue);
152
    }
177✔
153

177✔
154
    onInputValueChange(formatDate: string | null | Array<null>) {
55✔
155
        if (!formatDate || !formatDate.length) {
156
            const compatibleValue = formatDate ? (formatDate as CompatibleValue) : null;
157
            this.restoreTimePickerState(compatibleValue);
158
            super.onValueChange(compatibleValue);
1✔
159
            return;
160
        }
161
        let value = formatDate as string;
162
        const valueValid = isValidStringDate(value);
5✔
163
        const valueLimitValid = valueValid ? this.isValidDateLimit(parseStringDate(value)) : false;
1✔
164
        if (valueValid && valueLimitValid) {
165
            this.innerPreviousDate = value;
4✔
166
        } else {
167
            value = this.innerPreviousDate;
168
        }
13!
169
        const tinyDate = value ? (this.thyShowTime ? parseStringDate(value) : parseStringDate(value).startOfDay()) : null;
13✔
170
        this.restoreTimePickerState(tinyDate);
4✔
171
        super.onValueChange(tinyDate);
172
    }
13✔
173

174
    // Displays the time directly when the time must be displayed by default
175
    setDefaultTimePickerState(value: ThyPanelMode) {
176
        this.withTime = this.thyMustShowTime;
8✔
177
        if (this.isRange) {
8!
UNCOV
178
            this.panelMode = this.flexible ? ['date', 'date'] : [value, value];
×
179
        } else {
180
            this.panelMode = value;
8✔
181
        }
8✔
182
        this.showWeek = value === 'week';
8✔
183
        if (!this.thyFormat) {
184
            const inputFormats: { [key in ThyPanelMode]?: string } = {
185
                year: 'yyyy',
186
                quarter: 'yyyy-qqq',
1✔
187
                month: 'yyyy-MM',
188
                week: this.i18n.translate('datePicker.weekFormat'),
189
                date: this.thyShowTime ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd'
190
            };
191
            this.thyFormat = this.flexible ? inputFormats['date'] : inputFormats[value];
192
        }
193
    }
194

195
    // Restore after clearing time to select whether the original picker time is displayed or not
196
    restoreTimePickerState(value: CompatibleValue | null) {
197
        if (!value) {
198
            this.withTime = this.thyMustShowTime || this.originWithTime;
199
        }
1✔
200
    }
201

202
    // Emit thyOnCalendarChange when select date by thy-range-picker
203
    onCalendarChange(value: TinyDate[]): void {
204
        if (this.isRange) {
205
            const rangeValue = value.map(x => x.nativeDate);
206
            this.thyOnCalendarChange.emit(rangeValue);
207
        }
208
    }
209

210
    onShowTimePickerChange(show: boolean): void {
211
        this.withTime = show;
212
    }
213

214
    onResultOk(): void {
215
        if (this.isRange) {
216
            const value = this.thyValue as TinyDate[];
217
            if (value.length) {
218
                this.thyOnOk.emit([value[0].nativeDate, value[1].nativeDate]);
219
            } else {
220
                this.thyOnOk.emit([]);
221
            }
222
        } else {
223
            if (this.thyValue) {
224
                this.thyOnOk.emit((this.thyValue as TinyDate).nativeDate);
225
            } else {
226
                this.thyOnOk.emit(null);
227
            }
228
        }
229
        this.closeOverlay();
230
    }
231

232
    onOpenChange(open: boolean): void {
233
        this.thyOpenChange.emit(open);
234
        if (!open) {
235
            this.onTouchedFn();
236
        }
237
    }
238

239
    onFocus(event: Event) {
240
        this.picker.focus();
241
    }
242

243
    onBlur(event?: FocusEvent) {
244
        // Tab 聚焦后自动聚焦到 input 输入框,此分支下直接返回,无需触发 onTouchedFn
245
        if (elementMatchClosest(event?.relatedTarget as HTMLElement, ['date-popup', 'thy-picker'])) {
246
            return;
247
        }
248
        this.onTouchedFn();
249
    }
250

251
    onInputDate(value: string) {
252
        if (value && isValidStringDate(value)) {
253
            if (this.thyShowTime) {
254
                this.withTime = hasTimeInStringDate(value);
255
            }
256
            this.thyValue = parseStringDate(value);
257
        }
258
    }
259

260
    private isValidDateLimit(date: TinyDate): boolean {
261
        let disable = false;
262
        if (this.thyDisabledDate !== undefined) {
263
            disable = this.thyDisabledDate(date.nativeDate);
264
        }
265
        const minDate = this.thyMinDate ? new TinyDate(transformDateValue(this.thyMinDate).value as Date) : null;
266
        const maxDate = this.thyMaxDate ? new TinyDate(transformDateValue(this.thyMaxDate).value as Date) : null;
267
        return (
268
            (!minDate || date.startOfDay().nativeDate >= minDate.startOfDay().nativeDate) &&
269
            (!maxDate || date.startOfDay().nativeDate <= maxDate.startOfDay().nativeDate) &&
270
            !disable
271
        );
272
    }
273
}
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