• 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

3.06
/src/date-picker/abstract-picker.directive.ts
1
import { ThyPlacement } from 'ngx-tethys/core';
2
import { ThyPopover, ThyPopoverConfig } from 'ngx-tethys/popover';
3
import { 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, coerceBooleanProperty } from '@angular/cdk/coercion';
8
import {
9
    AfterViewInit,
10
    ChangeDetectorRef,
11
    Directive,
12
    ElementRef,
13
    EventEmitter,
14
    Input,
1✔
15
    OnChanges,
UNCOV
16
    OnDestroy,
×
UNCOV
17
    OnInit,
×
UNCOV
18
    Output,
×
UNCOV
19
    SimpleChange,
×
UNCOV
20
    TemplateRef,
×
UNCOV
21
    numberAttribute,
×
UNCOV
22
    inject
×
UNCOV
23
} from '@angular/core';
×
UNCOV
24

×
UNCOV
25
import { AbstractPickerComponent } from './abstract-picker.component';
×
UNCOV
26
import { DatePopup } from './lib/popups/date-popup.component';
×
UNCOV
27
import { ThyDateChangeEvent, ThyPanelMode } from './standard-types';
×
UNCOV
28
import { CompatibleValue } from './inner-types';
×
UNCOV
29
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
×
UNCOV
30

×
UNCOV
31
/**
×
32
 * @private
33
 */
UNCOV
34
@Directive()
×
35
export abstract class PickerDirective extends AbstractPickerComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {
36
    elementRef = inject(ElementRef);
UNCOV
37
    cdr: ChangeDetectorRef;
×
38
    private thyPopover = inject(ThyPopover);
39

UNCOV
40
    showWeek = false;
×
41

42
    @Input() thyDateRender: FunctionProp<TemplateRef<Date> | string>;
UNCOV
43

×
UNCOV
44
    panelMode: ThyPanelMode | ThyPanelMode[];
×
45

UNCOV
46
    /**
×
47
     * @type EventEmitter<ThyPanelMode | ThyPanelMode[]>
48
     */
UNCOV
49
    @Output() readonly thyOnPanelChange = new EventEmitter<ThyPanelMode | ThyPanelMode[]>();
×
UNCOV
50

×
51
    /**
UNCOV
52
     * @type EventEmitter<Date[]>
×
53
     */
54
    @Output() readonly thyOnCalendarChange = new EventEmitter<Date[]>();
UNCOV
55

×
56
    private _showTime: object | boolean;
57
    @Input() get thyShowTime(): object | boolean {
UNCOV
58
        return this._showTime;
×
UNCOV
59
    }
×
UNCOV
60
    set thyShowTime(value: object | boolean) {
×
UNCOV
61
        this._showTime = typeof value === 'object' ? value : coerceBooleanProperty(value);
×
62
    }
63

UNCOV
64
    /**
×
65
     * 是否展示时间(时、分)
UNCOV
66
     * @default false
×
67
     */
68
    @Input({ transform: coerceBooleanProperty }) thyMustShowTime = false;
UNCOV
69

×
UNCOV
70
    /**
×
71
     * 弹出位置
72
     * @type top | topLeft | topRight | bottom | bottomLeft | bottomRight | left | leftTop | leftBottom | right | rightTop | rightBottom
73
     */
74
    @Input() thyPlacement: ThyPlacement = 'bottom';
75

76
    private offset = 4;
77

78
    /**
79
     * 弹出 DatePicker 的偏移量
80
     * @default 4
81
     */
82
    @Input({ transform: numberAttribute })
83
    set thyOffset(value: number) {
84
        if (typeof ngDevMode === 'undefined' || ngDevMode) {
85
            warnDeprecation(`thyOffset parameter will be deprecated, please use thyPopoverOptions instead.`);
86
        }
87
        this.offset = value;
88
    }
89

90
    private hasBackdrop = true;
91

92
    /**
93
     * 是否有幕布
94
     * @default true
95
     */
96
    @Input({ transform: coerceBooleanProperty })
97
    set thyHasBackdrop(value: boolean) {
98
        if (typeof ngDevMode === 'undefined' || ngDevMode) {
99
            warnDeprecation(`thyHasBackdrop parameter will be deprecated, please use thyPopoverOptions instead.`);
UNCOV
100
        }
×
UNCOV
101
        this.hasBackdrop = value;
×
UNCOV
102
    }
×
UNCOV
103

×
UNCOV
104
    /**
×
UNCOV
105
     * popover 的其它参数
×
106
     */
UNCOV
107
    @Input() thyPopoverOptions: ThyPopoverConfig;
×
108

109
    /**
×
110
     * 是否阻止冒泡
UNCOV
111
     */
×
UNCOV
112
    @Input({ transform: coerceBooleanProperty }) thyStopPropagation = true;
×
UNCOV
113

×
114
    private destroy$ = new Subject<void>();
UNCOV
115
    private el: HTMLElement = this.elementRef.nativeElement;
×
116
    readonly $click: Observable<boolean> = fromEvent(this.el, 'click').pipe(
117
        tap(e => {
UNCOV
118
            if (this.thyStopPropagation) {
×
UNCOV
119
                e.stopPropagation();
×
120
            }
121
        }),
UNCOV
122
        mapTo(true)
×
123
    );
124

125
    takeUntilDestroyed = takeUntilDestroyed();
UNCOV
126

×
127
    ngOnInit() {
128
        this.getInitialState();
UNCOV
129
    }
×
UNCOV
130

×
UNCOV
131
    private getInitialState() {
×
132
        this.thyMode = this.thyMode || 'date';
133
        this.flexible = this.thyMode === 'flexible';
134

135
        if (this.isRange) {
UNCOV
136
            this.panelMode = this.flexible ? ['date', 'date'] : [this.thyMode, this.thyMode];
×
UNCOV
137
        } else {
×
138
            this.panelMode = this.thyMode;
139
        }
UNCOV
140
        this.showWeek = this.thyMode === 'week';
×
UNCOV
141
    }
×
142

143
    private openOverlay(): void {
UNCOV
144
        this.getInitialState();
×
UNCOV
145
        const popoverRef = this.thyPopover.open(
×
UNCOV
146
            DatePopup,
×
UNCOV
147
            Object.assign(
×
148
                {
149
                    origin: this.el,
150
                    hasBackdrop: this.hasBackdrop,
151
                    backdropClass: 'thy-overlay-transparent-backdrop',
UNCOV
152
                    offset: this.offset,
×
153
                    outsideClosable: true,
154
                    initialState: {
155
                        isRange: this.isRange,
UNCOV
156
                        panelMode: this.panelMode,
×
157
                        showWeek: this.showWeek,
×
158
                        value: this.thyValue,
159
                        showTime: this.thyShowTime,
160
                        mustShowTime: this.withTime,
161
                        format: this.thyFormat,
×
162
                        dateRender: this.thyDateRender,
163
                        disabledDate: this.thyDisabledDate,
1✔
164
                        placeholder: this.thyPlaceHolder,
165
                        className: this.thyPanelClassName,
166
                        defaultPickerValue: this.thyDefaultPickerValue,
167
                        minDate: this.thyMinDate,
168
                        maxDate: this.thyMaxDate,
169
                        showShortcut: this.thyShowShortcut,
170
                        shortcutPresets: this.shortcutPresets,
171
                        shortcutPosition: this.shortcutPosition,
172
                        flexible: this.flexible,
173
                        flexibleDateGranularity: this.flexibleDateGranularity,
174
                        timestampPrecision: this.thyTimestampPrecision
175
                    },
176
                    placement: this.thyPlacement
1✔
177
                },
178
                this.thyPopoverOptions
179
            )
180
        );
181
        if (popoverRef) {
182
            const componentInstance = popoverRef.componentInstance;
183
            componentInstance.valueChange.pipe(takeUntil(this.destroy$)).subscribe((event: CompatibleValue) => this.onValueChange(event));
184
            componentInstance.calendarChange.pipe(takeUntil(this.destroy$)).subscribe((event: CompatibleValue) => {
185
                const rangeValue = coerceArray(event).map(x => x.nativeDate);
186
                this.thyOnCalendarChange.emit(rangeValue);
187
            });
188
            componentInstance.showTimePickerChange
189
                .pipe(takeUntil(this.destroy$))
190
                .subscribe((event: boolean) => this.onShowTimePickerChange(event));
191
            // eslint-disable-next-line max-len
192
            componentInstance.ngOnChanges({ value: {} as SimpleChange }); // dynamically created components don't call ngOnChanges, manual call
193
            componentInstance.dateValueChange?.pipe(this.takeUntilDestroyed).subscribe((event: ThyDateChangeEvent) => {
194
                this.thyDateChange.emit(event);
195
            });
196
            popoverRef
197
                .afterOpened()
198
                .pipe(takeUntil(this.destroy$))
199
                .subscribe(() => this.thyOpenChange.emit(true));
200
            popoverRef
201
                .afterClosed()
202
                .pipe(takeUntil(this.destroy$))
203
                .subscribe(() => this.thyOpenChange.emit(false));
204
        }
205
    }
206

207
    closeOverlay(): void {
208
        this.thyPopover.close();
209
    }
210

211
    initActionSubscribe(): void {
212
        this.$click.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => {
213
            if (!this.thyDisabled && !this.thyReadonly) {
214
                this.openOverlay();
215
            }
216
        });
217
    }
218

219
    ngAfterViewInit(): void {
220
        this.setDefaultTimePickerState();
221
        this.initActionSubscribe();
222
    }
223

224
    ngOnDestroy(): void {
225
        this.destroy$.next();
226
        this.destroy$.complete();
227
    }
228

229
    onValueChange(value: CompatibleValue): void {
230
        this.restoreTimePickerState(value);
231
        super.onValueChange(value);
232
        if (!this.flexible) {
233
            this.closeOverlay();
234
        }
235
    }
236

237
    // Displays the time directly when the time must be displayed by default
238
    setDefaultTimePickerState() {
239
        this.withTime = this.thyMustShowTime;
240
    }
241

242
    // Restore after clearing time to select whether the original picker time is displayed or not
243
    restoreTimePickerState(value: CompatibleValue | null) {
244
        if (!value) {
245
            this.withTime = this.thyMustShowTime || this.originWithTime;
246
        }
247
    }
248
    onShowTimePickerChange(show: boolean): void {
249
        this.withTime = show;
250
    }
251
}
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