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

atinc / ngx-tethys / bd1b8e42-780e-4b88-adfb-82f93a2ca5b9

23 Aug 2023 06:27AM UTC coverage: 90.176% (+0.02%) from 90.157%
bd1b8e42-780e-4b88-adfb-82f93a2ca5b9

push

circleci

web-flow
feat(datePicker): #INFR-9165 support thyMode dynamically switched (#2770)

5110 of 6321 branches covered (80.84%)

Branch coverage included in aggregate %.

19 of 19 new or added lines in 3 files covered. (100.0%)

12908 of 13660 relevant lines covered (94.49%)

976.51 hits per line

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

85.15
/src/date-picker/abstract-picker.directive.ts
1
import { InputBoolean, InputNumber, ThyPlacement } from 'ngx-tethys/core';
2
import { ThyPopover, ThyPopoverConfig } from 'ngx-tethys/popover';
3
import { coerceBooleanProperty, FunctionProp, warnDeprecation } from 'ngx-tethys/util';
4
import { fromEvent, Observable, Subject } from 'rxjs';
5
import { debounceTime, mapTo, takeUntil, tap } from 'rxjs/operators';
6

7
import { coerceArray } from '@angular/cdk/coercion';
8
import {
9
    AfterViewInit,
10
    ChangeDetectorRef,
11
    Directive,
12
    ElementRef,
13
    EventEmitter,
14
    Input,
1✔
15
    OnChanges,
16
    OnDestroy,
22✔
17
    OnInit,
18
    Output,
19
    SimpleChange,
12!
20
    TemplateRef
21
} from '@angular/core';
22

12!
23
import { AbstractPickerComponent } from './abstract-picker.component';
12✔
24
import { DatePopupComponent } from './lib/popups/date-popup.component';
25
import { ThyPanelMode, ThyShortcutValueChange } from './standard-types';
12✔
26
import { CompatibleValue } from './inner-types';
27

28
/**
12!
29
 * @private
12✔
30
 */
31
@Directive()
12✔
32
export abstract class PickerDirective extends AbstractPickerComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {
33
    showWeek = false;
34

29!
35
    @Input() thyDateRender: FunctionProp<TemplateRef<Date> | string>;
29✔
36

29✔
37
    panelMode: ThyPanelMode | ThyPanelMode[];
15!
38

39
    /**
40
     * @type EventEmitter<ThyPanelMode | ThyPanelMode[]>
14✔
41
     */
42
    @Output() readonly thyOnPanelChange = new EventEmitter<ThyPanelMode | ThyPanelMode[]>();
29✔
43

44
    /**
45
     * @type EventEmitter<Date[]>
22✔
46
     */
47
    @Output() readonly thyOnCalendarChange = new EventEmitter<Date[]>();
48

49
    private _showTime: object | boolean;
50
    @Input() get thyShowTime(): object | boolean {
51
        return this._showTime;
52
    }
53
    set thyShowTime(value: object | boolean) {
54
        this._showTime = typeof value === 'object' ? value : coerceBooleanProperty(value);
55
    }
56

57
    /**
58
     * 是否展示时间(时、分)
59
     * @default false
60
     */
61
    @Input() @InputBoolean() thyMustShowTime = false;
62

63
    /**
64
     * 弹出位置
65
     * @type top | topLeft | topRight | bottom | bottomLeft | bottomRight | left | leftTop | leftBottom | right | rightTop | rightBottom
66
     */
67
    @Input() thyPlacement: ThyPlacement = 'bottom';
68

69
    private offset = 4;
70

71
    /**
72
     * 弹出 DatePicker 的偏移量
73
     * @default 4
22!
74
     */
22✔
75
    @Input()
22✔
76
    @InputNumber()
22✔
77
    set thyOffset(value: number) {
12✔
78
        if (typeof ngDevMode === 'undefined' || ngDevMode) {
8✔
79
            warnDeprecation(`thyOffset parameter will be deprecated, please use thyPopoverOptions instead.`);
80
        }
22✔
81
        this.offset = value;
82
    }
×
83

84
    private hasBackdrop = true;
22✔
85

22✔
86
    /**
×
87
     * 是否有幕布
88
     * @default false
22✔
89
     */
90
    @Input()
91
    @InputBoolean()
18✔
92
    set thyHasBackdrop(value: boolean) {
22✔
93
        if (typeof ngDevMode === 'undefined' || ngDevMode) {
94
            warnDeprecation(`thyHasBackdrop parameter will be deprecated, please use thyPopoverOptions instead.`);
95
        }
5✔
96
        this.hasBackdrop = value;
97
    }
98

99
    /**
4✔
100
     * popover 的其他参数
101
     */
102
    @Input() thyPopoverOptions: ThyPopoverConfig;
29✔
103

22!
104
    /**
22✔
105
     * 是否阻止冒泡
106
     */
107
    @Input() @InputBoolean() thyStopPropagation = true;
108

109
    private destroy$ = new Subject<void>();
30✔
110
    private el: HTMLElement = this.elementRef.nativeElement;
30✔
111
    readonly $click: Observable<boolean> = fromEvent(this.el, 'click').pipe(
30✔
112
        tap(e => {
30✔
113
            if (this.thyStopPropagation) {
30✔
114
                e.stopPropagation();
30✔
115
            }
30✔
116
        }),
30✔
117
        mapTo(true)
30✔
118
    );
30✔
119

30✔
120
    ngOnInit() {
30✔
121
        this.thyMode = this.thyMode || 'date';
30✔
122
        this.flexible = this.thyMode === 'flexible';
30✔
123

30✔
124
        if (this.isRange) {
24✔
125
            this.panelMode = this.flexible ? ['date', 'date'] : [this.thyMode, this.thyMode];
23✔
126
        } else {
127
            this.panelMode = this.thyMode;
128
        }
129
        this.showWeek = this.thyMode === 'week';
130
    }
29✔
131

29✔
132
    private openOverlay(): void {
133
        const popoverRef = this.thyPopover.open(
134
            DatePopupComponent,
30✔
135
            Object.assign(
30✔
136
                {
137
                    origin: this.el,
138
                    hasBackdrop: this.hasBackdrop,
4✔
139
                    backdropClass: 'thy-overlay-transparent-backdrop',
4✔
140
                    offset: this.offset,
4!
141
                    initialState: {
4✔
142
                        isRange: this.isRange,
143
                        panelMode: this.panelMode,
144
                        showWeek: this.showWeek,
145
                        value: this.thyValue,
146
                        showTime: this.thyShowTime,
29✔
147
                        mustShowTime: this.withTime,
148
                        format: this.thyFormat,
149
                        dateRender: this.thyDateRender,
150
                        disabledDate: this.thyDisabledDate,
4!
151
                        placeholder: this.thyPlaceHolder,
×
152
                        className: this.thyPanelClassName,
153
                        defaultPickerValue: this.thyDefaultPickerValue,
154
                        minDate: this.thyMinDate,
155
                        maxDate: this.thyMaxDate,
×
156
                        showShortcut: this.thyShowShortcut,
157
                        shortcutPresets: this.shortcutPresets,
1✔
158
                        shortcutPosition: this.shortcutPosition,
159
                        flexible: this.flexible,
160
                        flexibleDateGranularity: this.flexibleDateGranularity
161
                    },
162
                    placement: this.thyPlacement
1✔
163
                },
164
                this.thyPopoverOptions
165
            )
166
        );
167
        if (popoverRef) {
168
            const componentInstance = popoverRef.componentInstance;
169
            componentInstance.valueChange.pipe(takeUntil(this.destroy$)).subscribe((event: CompatibleValue) => this.onValueChange(event));
170
            componentInstance.calendarChange.pipe(takeUntil(this.destroy$)).subscribe((event: CompatibleValue) => {
171
                const rangeValue = coerceArray(event).map(x => x.nativeDate);
172
                this.thyOnCalendarChange.emit(rangeValue);
173
            });
174
            componentInstance.showTimePickerChange
175
                .pipe(takeUntil(this.destroy$))
1✔
176
                .subscribe((event: boolean) => this.onShowTimePickerChange(event));
177
            // eslint-disable-next-line max-len
178
            componentInstance.ngOnChanges({ value: {} as SimpleChange }); // dynamically created components don't call ngOnChanges, manual call
179
            componentInstance.shortcutValueChange?.pipe(takeUntil(this.destroy$)).subscribe((event: ThyShortcutValueChange) => {
1✔
180
                this.thyShortcutValueChange.emit(event);
181
            });
182
            popoverRef
183
                .afterOpened()
184
                .pipe(takeUntil(this.destroy$))
1✔
185
                .subscribe(() => this.thyOpenChange.emit(true));
186
            popoverRef
187
                .afterClosed()
188
                .pipe(takeUntil(this.destroy$))
189
                .subscribe(() => this.thyOpenChange.emit(false));
1✔
190
        }
191
    }
192

193
    closeOverlay(): void {
1✔
194
        this.thyPopover.close();
195
    }
196

197
    initActionSubscribe(): void {
198
        this.$click.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => {
199
            if (!this.thyDisabled && !this.thyReadonly) {
200
                this.openOverlay();
201
            }
202
        });
203
    }
204

205
    constructor(public elementRef: ElementRef, public cdr: ChangeDetectorRef, private thyPopover: ThyPopover) {
206
        super(cdr);
207
    }
208

209
    ngAfterViewInit(): void {
210
        this.setDefaultTimePickerState();
211
        this.initActionSubscribe();
212
    }
213

214
    ngOnDestroy(): void {
215
        this.destroy$.next();
216
        this.destroy$.complete();
217
    }
218

219
    onValueChange(value: CompatibleValue): void {
220
        this.restoreTimePickerState(value);
221
        super.onValueChange(value);
222
        if (!this.flexible) {
223
            this.closeOverlay();
224
        }
225
    }
226

227
    // Displays the time directly when the time must be displayed by default
228
    setDefaultTimePickerState() {
229
        this.withTime = this.thyMustShowTime;
230
    }
231

232
    // Restore after clearing time to select whether the original picker time is displayed or not
233
    restoreTimePickerState(value: CompatibleValue | null) {
234
        if (!value) {
235
            this.withTime = this.thyMustShowTime || this.originWithTime;
236
        }
237
    }
238
    onShowTimePickerChange(show: boolean): void {
239
        this.withTime = show;
240
    }
241
}
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