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

atinc / ngx-tethys / eaebe679-eeb4-4c09-8c03-618fee19a08f

18 Nov 2024 06:00AM UTC coverage: 90.347% (-0.005%) from 90.352%
eaebe679-eeb4-4c09-8c03-618fee19a08f

Pull #3242

circleci

minlovehua
feat(i18n): i18n design and review #TINFR-916 
Pull Request #3242: feat(i18n): i18n design and review #TINFR-916 

5522 of 6760 branches covered (81.69%)

Branch coverage included in aggregate %.

32 of 35 new or added lines in 10 files covered. (91.43%)

20 existing lines in 3 files now uncovered.

13196 of 13958 relevant lines covered (94.54%)

996.5 hits per line

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

82.12
/src/date-picker/abstract-picker.component.ts
1
import { TabIndexDisabledControlValueAccessorMixin } from 'ngx-tethys/core';
2
import { coerceBooleanProperty, TinyDate } from 'ngx-tethys/util';
3
import { Subject } from 'rxjs';
4

5
import {
6
    ChangeDetectorRef,
7
    Directive,
8
    EventEmitter,
9
    inject,
10
    Input,
11
    OnChanges,
12
    OnDestroy,
13
    OnInit,
1✔
14
    Output,
15
    Signal,
69✔
16
    SimpleChanges,
17
    ViewChild
18
} from '@angular/core';
269✔
19
import { ControlValueAccessor } from '@angular/forms';
20

21
import { CompatibleValue, RangeAdvancedValue } from './inner-types';
66!
22
import { ThyPicker } from './picker.component';
66✔
23
import { makeValue, setValueByTimestampPrecision, transformDateValue } from './picker.util';
24
import {
25
    CompatibleDate,
26
    DateEntry,
66✔
27
    DisabledDateFn,
28
    ThyDateRangeEntry,
29
    ThyPanelMode,
342✔
30
    ThyShortcutPosition,
31
    CompatiblePresets,
32
    ThyDateGranularity,
3,026✔
33
    ThyDateChangeEvent
34
} from './standard-types';
35
import { ThyDatePickerConfigService } from './date-picker.service';
13✔
36
import { SafeAny } from 'ngx-tethys/types';
37
import { injectLocale, ThyDatePickerLocale } from 'ngx-tethys/i18n';
38

691✔
39
/**
40
 * @private
41
 */
153✔
42
@Directive()
43
export abstract class AbstractPickerComponent
44
    extends TabIndexDisabledControlValueAccessorMixin
187✔
45
    implements OnInit, OnChanges, OnDestroy, ControlValueAccessor
187✔
46
{
187✔
47
    cdr = inject(ChangeDetectorRef);
187✔
48

187✔
49
    locale: Signal<ThyDatePickerLocale> = injectLocale('datePicker');
187✔
50

187✔
51
    thyValue: CompatibleValue | null;
187✔
52

187!
53
    _panelMode: ThyPanelMode = 'date';
187✔
54

187✔
55
    private datePickerConfigService = inject(ThyDatePickerConfigService);
187✔
56

187✔
57
    /**
187✔
58
     * 模式
187✔
59
     * @type decade | year | month | date | week | flexible
187✔
60
     */
187✔
61
    @Input() set thyMode(value: ThyPanelMode) {
187✔
62
        this._panelMode = value ?? 'date';
187✔
63
    }
187✔
64

65
    get thyMode() {
66
        return this._panelMode;
153✔
67
    }
153✔
68

153✔
69
    /**
70
     * 是否显示清除按钮
71
     */
153✔
72
    @Input({ transform: coerceBooleanProperty }) thyAllowClear = true;
73

74
    /**
41✔
75
     * 是否自动获取焦点
76
     * @default false
77
     */
230✔
78
    @Input({ transform: coerceBooleanProperty }) thyAutoFocus = false;
37✔
79

80
    @Input({ transform: coerceBooleanProperty }) thyOpen: boolean;
81

82
    @Input() thyDisabledDate: DisabledDateFn;
153✔
83

153✔
84
    /**
85
     * 最小值
86
     * @type Date | number
45✔
87
     */
88
    @Input() thyMinDate: Date | number;
89

UNCOV
90
    /**
×
91
     * 最大值
92
     * @type Date | number
×
93
     */
94
    @Input() thyMaxDate: Date | number;
95

UNCOV
96
    /**
×
97
     * 输入框提示文字
98
     * @type string | string[]
×
99
     */
100
    @Input() thyPlaceHolder: string | string[];
101

UNCOV
102
    /**
×
103
     * 是否只读
104
     * @default false
×
105
     */
106
    @Input({ transform: coerceBooleanProperty }) thyReadonly: boolean;
107

UNCOV
108
    /**
×
109
     * 选择器 className
110
     */
×
111
    @Input() thyOriginClassName: string;
112

113
    /**
UNCOV
114
     * 弹出层 className
×
115
     */
116
    @Input() thyPanelClassName: string;
×
117

118
    /**
119
     * 输入框的大小
UNCOV
120
     * @type xs | sm | md | lg | default
×
121
     */
122
    @Input() thySize: 'lg' | 'md' | 'sm' | 'xs' | 'default' = 'default';
×
123

124
    /**
125
     * 设置时间戳精度
70✔
126
     * @default seconds 10位
70✔
127
     */
70✔
128
    @Input() thyTimestampPrecision: 'seconds' | 'milliseconds' = this.datePickerConfigService.config?.timestampPrecision || 'seconds';
70✔
129

70✔
130
    /**
28✔
131
     * 展示的日期格式
28✔
132
     * @default yyyy-MM-dd
28✔
133
     */
25✔
134
    @Input() thyFormat: string;
25!
UNCOV
135

×
136
    /**
137
     * @description.en-us only for range picker, Whether to automatically take the beginning and ending unixTime of the day
138
     * @description.zh-cn 是否取值开始日期的00:00以及截止日期的24:00
25✔
139
     * @default false
140
     */
141
    @Input({ transform: coerceBooleanProperty }) thyAutoStartAndEnd = false;
142

143
    /**
144
     * 面板默认日期
28✔
145
     * @type CompatibleDate | number | null
28✔
146
     */
147
    @Input() thyDefaultPickerValue: CompatibleDate | number | null = null;
148

42✔
149
    /**
42✔
150
     * 自定义的后缀图标
32✔
151
     */
152
    @Input() thySuffixIcon = 'calendar';
42✔
153

39✔
154
    /**
155
     * 是否展示快捷选项面板
156
     * @default false
3✔
157
     */
158
    @Input({ transform: coerceBooleanProperty }) thyShowShortcut: boolean;
159

160
    /**
161
     * 快捷选项面板的显示位置
450✔
162
     * @type left | bottom
74!
UNCOV
163
     */
×
164
    @Input() set thyShortcutPosition(position: ThyShortcutPosition) {
165
        if (!!position) {
166
            this.shortcutPosition = position;
74!
UNCOV
167
        }
×
168
    }
169

170
    /**
171
     * 自定义快捷选项
172
     * @type ThyShortcutPreset[]
UNCOV
173
     */
×
UNCOV
174
    @Input() set thyShortcutPresets(presets: CompatiblePresets) {
×
175
        this.shortcutPresets = presets;
176
    }
177

380✔
178
    /**
380✔
179
     * 日期变化的回调
380✔
180
     */
3✔
181
    @Output() readonly thyDateChange = new EventEmitter<ThyDateChangeEvent>();
2✔
182

183
    @Output() readonly thyOpenChange = new EventEmitter<boolean>();
184

380✔
185
    @ViewChild(ThyPicker, { static: true }) public picker: ThyPicker;
380✔
186

380✔
187
    /**
380✔
188
     * 是否禁用
380✔
189
     * @default false
380✔
190
     */
191
    @Input({ transform: coerceBooleanProperty })
192
    set thyDisabled(value: boolean) {
381✔
193
        this.disabled = value;
194
    }
195
    get thyDisabled(): boolean {
182✔
196
        return this.disabled;
182✔
197
    }
198

199
    disabled = false;
153✔
200

116✔
201
    shortcutPosition: ThyShortcutPosition = 'left';
202

153✔
203
    shortcutPresets: CompatiblePresets;
204

205
    isRange: boolean;
450✔
206

207
    withTime: boolean;
208

70✔
209
    flexible: boolean;
210

1✔
211
    flexibleDateGranularity: ThyDateGranularity;
1✔
212

213
    protected destroyed$: Subject<void> = new Subject();
214
    protected isCustomPlaceHolder = false;
215
    private onlyEmitDate = false;
216
    protected originWithTime: boolean;
217

218
    get realOpenState(): boolean {
219
        return this.picker.realOpenState;
220
    }
221

222
    get isShowDatePopup(): boolean {
223
        return this.picker.isShowDatePopup;
224
    }
225

226
    initValue(): void {
227
        this.thyValue = this.isRange ? [] : null;
228
    }
229

230
    constructor() {
231
        super();
232
    }
233

234
    ngOnInit(): void {
235
        this.setDefaultPlaceHolder();
236
        this.initValue();
237
        this.isFlexible();
238
    }
1✔
239

240
    isFlexible() {
241
        this.flexible = this.thyMode === 'flexible';
242
    }
243

244
    onDateValueChange(event: ThyDateChangeEvent) {
245
        this.thyDateChange.emit(event);
246
    }
247

248
    ngOnChanges(changes: SimpleChanges): void {
249
        if (changes.thyPlaceHolder && changes.thyPlaceHolder.firstChange && typeof this.thyPlaceHolder !== 'undefined') {
250
            this.isCustomPlaceHolder = true;
251
        }
252
    }
253

254
    ngOnDestroy(): void {
255
        this.destroyed$.next();
256
        this.destroyed$.complete();
257
    }
258

259
    closeOverlay(): void {
260
        this.picker.hideOverlay();
261
    }
262

263
    getAutoStartAndEndValue(begin: TinyDate, end: TinyDate) {
264
        let value: { begin: number; end: number };
265
        switch (this.thyMode) {
266
            case 'date':
267
                value = {
268
                    begin: begin.startOfDay().getUnixTime(),
269
                    end: end.endOfDay().getUnixTime()
270
                };
271
                break;
272
            case 'week':
273
                value = {
274
                    begin: begin.startOfWeek().getUnixTime(),
275
                    end: end.endOfWeek().getUnixTime()
276
                };
277
                break;
278
            case 'month':
279
                value = {
280
                    begin: begin.startOfMonth().getUnixTime(),
281
                    end: end.endOfMonth().getUnixTime()
282
                };
283
                break;
284
            case 'year':
285
                value = {
286
                    begin: begin.startOfYear().getUnixTime(),
287
                    end: end.endOfYear().getUnixTime()
288
                };
289
                break;
290
            default:
291
                value = {
292
                    begin: begin.startOfDay().getUnixTime(),
293
                    end: end.endOfDay().getUnixTime()
294
                };
295
                break;
296
        }
297
        return value;
298
    }
299

300
    onValueChange(originalValue: CompatibleValue | RangeAdvancedValue): void {
301
        this.setFormatRule();
302
        const { value, withTime, flexibleDateGranularity } = transformDateValue(originalValue);
303
        this.flexibleDateGranularity = flexibleDateGranularity;
304
        this.setValue(value);
305
        if (this.isRange) {
306
            const vAsRange: any = this.thyValue;
307
            let value = { begin: null, end: null } as ThyDateRangeEntry;
308
            if (vAsRange.length) {
309
                const [begin, end] = vAsRange as TinyDate[];
310
                if (this.thyAutoStartAndEnd) {
311
                    value = this.getAutoStartAndEndValue(begin, end);
312
                } else {
313
                    value = {
314
                        begin: begin.getUnixTime(),
315
                        end: end.getUnixTime()
316
                    };
317
                }
318
            }
319
            const [beginUnixTime, endUnixTime] = this.setValueByPrecision(value) as number[];
320
            this.onChangeFn(
321
                Object.assign({ begin: beginUnixTime, end: endUnixTime }, this.flexible ? { granularity: flexibleDateGranularity } : {})
322
            );
323
        } else {
324
            const value = { date: null, with_time: this.withTime ? 1 : 0 } as DateEntry;
325
            if (this.thyValue) {
326
                value.date = (this.thyValue as TinyDate).getUnixTime();
327
            }
328
            if (this.onlyEmitDate) {
329
                this.onChangeFn(this.setValueByPrecision(value.date) as number);
330
            } else {
331
                this.onChangeFn(Object.assign(value, { date: this.setValueByPrecision(value.date) as number }));
332
            }
333
        }
334
    }
335

336
    setFormatRule() {
337
        if (!this.thyFormat) {
338
            if (this.withTime) {
339
                this.thyFormat = 'yyyy-MM-dd HH:mm';
340
            } else {
341
                if (!this.onlyEmitDate) {
342
                    this.thyFormat = 'yyyy-MM-dd';
343
                }
344
            }
345
        }
346
    }
347

348
    onOpenChange(open: boolean): void {
349
        this.thyOpen = open;
350
        this.thyOpenChange.emit(open);
351
    }
352

353
    onChangeFn: (val: CompatibleDate | DateEntry | ThyDateRangeEntry | number | null) => void = () => void 0;
354

355
    writeValue(originalValue: CompatibleDate | ThyDateRangeEntry): void {
356
        const { value, withTime, flexibleDateGranularity } = transformDateValue(originalValue);
357
        this.flexibleDateGranularity = flexibleDateGranularity;
358
        if (this.flexible && value && (value as Date[]).length) {
359
            if (!this.flexibleDateGranularity) {
360
                this.flexibleDateGranularity = 'day';
361
            }
362
        }
363

364
        this.setValue(value);
365
        this.setTimePickerState(withTime);
366
        this.onlyEmitDate = typeof withTime === 'undefined';
367
        this.originWithTime = withTime;
368
        this.setFormatRule();
369
        this.cdr.markForCheck();
370
    }
371

372
    setTimePickerState(withTime: boolean): void {
373
        this.withTime = withTime;
374
    }
375

376
    setDisabledState(disabled: boolean): void {
377
        this.thyDisabled = disabled;
378
        this.cdr.markForCheck();
379
    }
380

381
    private setDefaultPlaceHolder(): void {
382
        if (!this.isCustomPlaceHolder) {
383
            this.thyPlaceHolder = this.isRange ? ['开始日期', '结束日期'] : '请选择日期';
384
        }
385
        this.cdr.markForCheck();
386
    }
387

388
    public setValue(value: CompatibleDate): void {
389
        this.thyValue = makeValue(value, this.isRange);
390
    }
391

392
    private setValueByPrecision(value: CompatibleDate | number | Date | DateEntry | ThyDateRangeEntry | SafeAny): number | number[] {
393
        return setValueByTimestampPrecision(value, this.isRange, this.thyTimestampPrecision);
394
    }
395
}
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