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

atinc / ngx-tethys / 8a6ba229-c82f-4a21-a1ed-95461f2ad66c

04 Sep 2023 08:37AM UTC coverage: 90.196% (-0.004%) from 90.2%
8a6ba229-c82f-4a21-a1ed-95461f2ad66c

Pull #2829

circleci

cmm-va
fix: delete f
Pull Request #2829: fix: add tabIndex

5164 of 6386 branches covered (0.0%)

Branch coverage included in aggregate %.

78 of 78 new or added lines in 26 files covered. (100.0%)

13024 of 13779 relevant lines covered (94.52%)

971.69 hits per line

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

82.89
/src/date-picker/abstract-picker.component.ts
1
import { InputBoolean, 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
    Input,
10
    OnChanges,
11
    OnDestroy,
1✔
12
    OnInit,
13
    Output,
37✔
14
    SimpleChanges,
15
    ViewChild
16
} from '@angular/core';
131✔
17
import { ControlValueAccessor } from '@angular/forms';
18

19
import { CompatibleValue, RangeAdvancedValue } from './inner-types';
60!
20
import { ThyPickerComponent } from './picker.component';
60✔
21
import { makeValue, transformDateValue } from './picker.util';
22
import {
23
    CompatibleDate,
24
    DateEntry,
60✔
25
    DisabledDateFn,
26
    ThyDateRangeEntry,
27
    ThyPanelMode,
2,257✔
28
    ThyShortcutPosition,
29
    CompatiblePresets,
30
    ThyShortcutValueChange,
275✔
31
    ThyDateGranularity
32
} from './standard-types';
33

507✔
34
/**
35
 * @private
36
 */
123✔
37
@Directive()
38
export abstract class AbstractPickerComponent
39
    extends TabIndexDisabledControlValueAccessorMixin
153✔
40
    implements OnInit, OnChanges, OnDestroy, ControlValueAccessor
153✔
41
{
153✔
42
    thyValue: CompatibleValue | null;
153✔
43

153✔
44
    _panelMode: ThyPanelMode = 'date';
153✔
45

153✔
46
    /**
153✔
47
     * 模式
153✔
48
     * @type decade | year | month | date | week | flexible
153✔
49
     */
153✔
50
    @Input() set thyMode(value: ThyPanelMode) {
153✔
51
        this._panelMode = value ?? 'date';
153✔
52
    }
153✔
53

153✔
54
    get thyMode() {
153✔
55
        return this._panelMode;
153✔
56
    }
57

58
    /**
123✔
59
     * 是否显示清除按钮
123✔
60
     */
123✔
61
    @Input() @InputBoolean() thyAllowClear = true;
62

63
    /**
123✔
64
     * 是否自动获取焦点
65
     * @default false
66
     */
15✔
67
    @Input() @InputBoolean() thyAutoFocus = false;
68

69
    @Input() @InputBoolean() thyOpen: boolean;
188✔
70

24✔
71
    @Input() thyDisabledDate: DisabledDateFn;
72

73
    /**
74
     * 最小值
123✔
75
     * @type Date | number
123✔
76
     */
77
    @Input() thyMinDate: Date | number;
78

42✔
79
    /**
80
     * 最大值
81
     * @type Date | number
82
     */
×
83
    @Input() thyMaxDate: Date | number;
84

×
85
    /**
86
     * 输入框提示文字
87
     * @type string | string[]
88
     */
×
89
    @Input() thyPlaceHolder: string | string[];
90

×
91
    /**
92
     * 是否只读
93
     * @default false
94
     */
×
95
    @Input() @InputBoolean() thyReadonly: boolean;
96

×
97
    /**
98
     * 选择器 className
99
     */
100
    @Input() thyOriginClassName: string;
×
101

102
    /**
×
103
     * 弹出层 className
104
     */
105
    @Input() thyPanelClassName: string;
106

×
107
    /**
108
     * 输入框的大小
×
109
     * @type xs | sm | md | lg | default
110
     */
111
    @Input() thySize: 'lg' | 'md' | 'sm' | 'xs' | 'default' = 'default';
112

×
113
    /**
114
     * 展示的日期格式
×
115
     * @default yyyy-MM-dd
116
     */
117
    @Input() thyFormat: string;
47✔
118

47✔
119
    /**
47✔
120
     * @description.en-us only for range picker, Whether to automatically take the beginning and ending unixTime of the day
47✔
121
     * @description.zh-cn 是否取值开始日期的00:00以及截止日期的24:00
47✔
122
     * @default false
25✔
123
     */
25✔
124
    @Input() @InputBoolean() thyAutoStartAndEnd = false;
25✔
125

22✔
126
    /**
22!
127
     * 面板默认日期
×
128
     * @type CompatibleDate | number | null
129
     */
130
    @Input() thyDefaultPickerValue: CompatibleDate | number | null = null;
22✔
131

132
    /**
133
     * 自定义的后缀图标
134
     */
135
    @Input() thySuffixIcon = 'calendar';
136

25✔
137
    /**
6✔
138
     * 是否展示快捷选项面板
139
     * @default false
25✔
140
     */
141
    @Input() @InputBoolean() thyShowShortcut: boolean;
142

22✔
143
    /**
22✔
144
     * 快捷选项面板的显示位置
16✔
145
     * @type left | bottom
146
     */
22✔
147
    @Input() set thyShortcutPosition(position: ThyShortcutPosition) {
21✔
148
        if (!!position) {
149
            this.shortcutPosition = position;
150
        }
1✔
151
    }
152

153
    /**
154
     * 自定义快捷选项
155
     * @type ThyShortcutPreset[]
355✔
156
     */
61!
157
    @Input() set thyShortcutPresets(presets: CompatiblePresets) {
×
158
        this.shortcutPresets = presets;
159
    }
160

61!
161
    @Output() readonly thyShortcutValueChange = new EventEmitter<ThyShortcutValueChange>();
×
162

163
    @Output() readonly thyOpenChange = new EventEmitter<boolean>();
164

165
    @ViewChild(ThyPickerComponent, { static: true }) public picker: ThyPickerComponent;
166

167
    /**
×
168
     * 是否禁用
×
169
     * @default false
170
     */
171
    @Input()
308✔
172
    @InputBoolean()
308✔
173
    get thyDisabled(): boolean {
308✔
174
        return this.disabled;
3✔
175
    }
2✔
176
    set thyDisabled(value: boolean) {
177
        this.disabled = coerceBooleanProperty(value);
178
    }
308✔
179

308✔
180
    disabled = false;
308✔
181

308✔
182
    shortcutPosition: ThyShortcutPosition = 'left';
308✔
183

308✔
184
    shortcutPresets: CompatiblePresets;
185

186
    isRange: boolean;
308✔
187

188
    withTime: boolean;
189

148✔
190
    flexible: boolean;
148✔
191

192
    flexibleDateGranularity: ThyDateGranularity;
193

123✔
194
    protected destroyed$: Subject<void> = new Subject();
99✔
195
    protected isCustomPlaceHolder = false;
196
    private onlyEmitDate = false;
123✔
197
    protected originWithTime: boolean;
198

199
    get realOpenState(): boolean {
355✔
200
        return this.picker.realOpenState;
201
    }
1✔
202

203
    initValue(): void {
204
        this.thyValue = this.isRange ? [] : null;
1✔
205
    }
206

207
    constructor(public cdr: ChangeDetectorRef) {
208
        super();
209
    }
210

211
    ngOnInit(): void {
212
        this.setDefaultPlaceHolder();
213
        this.initValue();
214
        this.isFlexible();
215
    }
216

217
    isFlexible() {
218
        this.flexible = this.thyMode === 'flexible';
219
    }
220

221
    onShortcutValueChange(event: ThyShortcutValueChange) {
222
        this.thyShortcutValueChange.emit(event);
223
    }
224

225
    ngOnChanges(changes: SimpleChanges): void {
226
        if (changes.thyPlaceHolder && changes.thyPlaceHolder.firstChange && typeof this.thyPlaceHolder !== 'undefined') {
227
            this.isCustomPlaceHolder = true;
228
        }
229
    }
230

1✔
231
    ngOnDestroy(): void {
232
        this.destroyed$.next();
233
        this.destroyed$.complete();
234
    }
1✔
235

236
    closeOverlay(): void {
237
        this.picker.hideOverlay();
238
    }
1✔
239

240
    getAutoStartAndEndValue(begin: TinyDate, end: TinyDate) {
241
        let value: { begin: number; end: number };
242
        switch (this.thyMode) {
1✔
243
            case 'date':
244
                value = {
245
                    begin: begin.startOfDay().getUnixTime(),
246
                    end: end.endOfDay().getUnixTime()
1✔
247
                };
248
                break;
249
            case 'week':
250
                value = {
1✔
251
                    begin: begin.startOfWeek().getUnixTime(),
252
                    end: end.endOfWeek().getUnixTime()
253
                };
254
                break;
1✔
255
            case 'month':
256
                value = {
257
                    begin: begin.startOfMonth().getUnixTime(),
258
                    end: end.endOfMonth().getUnixTime()
259
                };
1✔
260
                break;
261
            case 'year':
262
                value = {
263
                    begin: begin.startOfYear().getUnixTime(),
264
                    end: end.endOfYear().getUnixTime()
265
                };
266
                break;
267
            default:
268
                value = {
269
                    begin: begin.startOfDay().getUnixTime(),
270
                    end: end.endOfDay().getUnixTime()
271
                };
272
                break;
273
        }
274
        return value;
275
    }
276

277
    onValueChange(originalValue: CompatibleValue | RangeAdvancedValue): void {
278
        this.setFormatRule();
279
        const { value, withTime, flexibleDateGranularity } = transformDateValue(originalValue);
280
        this.flexibleDateGranularity = flexibleDateGranularity;
281
        this.setValue(value);
282
        if (this.isRange) {
283
            const vAsRange: any = this.thyValue;
284
            let value = { begin: null, end: null } as ThyDateRangeEntry;
285
            if (vAsRange.length) {
286
                const [begin, end] = vAsRange as TinyDate[];
287
                if (this.thyAutoStartAndEnd) {
288
                    value = this.getAutoStartAndEndValue(begin, end);
289
                } else {
290
                    value = {
291
                        begin: begin.getUnixTime(),
292
                        end: end.getUnixTime()
293
                    };
294
                }
295
            }
296
            if (this.flexible) {
297
                value.granularity = flexibleDateGranularity;
298
            }
299
            this.onChangeFn(value);
300
        } else {
301
            const value = { date: null, with_time: this.withTime ? 1 : 0 } as DateEntry;
302
            if (this.thyValue) {
303
                value.date = (this.thyValue as TinyDate).getUnixTime();
304
            }
305
            if (this.onlyEmitDate) {
306
                this.onChangeFn(value.date);
307
            } else {
308
                this.onChangeFn(value);
309
            }
310
        }
311
    }
312

313
    setFormatRule() {
314
        if (!this.thyFormat) {
315
            if (this.withTime) {
316
                this.thyFormat = 'yyyy-MM-dd HH:mm';
317
            } else {
318
                if (!this.onlyEmitDate) {
319
                    this.thyFormat = 'yyyy-MM-dd';
320
                }
321
            }
322
        }
323
    }
324

325
    onOpenChange(open: boolean): void {
326
        this.thyOpen = open;
327
        this.thyOpenChange.emit(open);
328
    }
329

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

332
    writeValue(originalValue: CompatibleDate | ThyDateRangeEntry): void {
333
        const { value, withTime, flexibleDateGranularity } = transformDateValue(originalValue);
334
        this.flexibleDateGranularity = flexibleDateGranularity;
335
        if (this.flexible && value && (value as Date[]).length) {
336
            if (!this.flexibleDateGranularity) {
337
                this.flexibleDateGranularity = 'day';
338
            }
339
        }
340

341
        this.setValue(value);
342
        this.setTimePickerState(withTime);
343
        this.onlyEmitDate = typeof withTime === 'undefined';
344
        this.originWithTime = withTime;
345
        this.setFormatRule();
346
        this.cdr.markForCheck();
347
    }
348

349
    setTimePickerState(withTime: boolean): void {
350
        this.withTime = withTime;
351
    }
352

353
    setDisabledState(disabled: boolean): void {
354
        this.thyDisabled = disabled;
355
        this.cdr.markForCheck();
356
    }
357

358
    private setDefaultPlaceHolder(): void {
359
        if (!this.isCustomPlaceHolder) {
360
            this.thyPlaceHolder = this.isRange ? ['开始日期', '结束日期'] : '请选择日期';
361
        }
362
        this.cdr.markForCheck();
363
    }
364

365
    public setValue(value: CompatibleDate): void {
366
        this.thyValue = makeValue(value, this.isRange);
367
    }
368
}
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