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

atinc / ngx-tethys / d9ae709b-3c27-4b69-b125-b8b80b54f90b

pending completion
d9ae709b-3c27-4b69-b125-b8b80b54f90b

Pull #2757

circleci

mengshuicmq
fix: fix code review
Pull Request #2757: feat(color-picker): color-picker support disabled (#INFR-8645)

98 of 6315 branches covered (1.55%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

2392 of 13661 relevant lines covered (17.51%)

83.12 hits per line

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

4.17
/src/time-picker/time-picker-panel.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    ChangeDetectorRef,
4
    Component,
5
    ElementRef,
6
    EventEmitter,
7
    forwardRef,
8
    Input,
9
    NgZone,
10
    OnDestroy,
11
    OnInit,
12
    Output,
13
    ViewChild
14
} from '@angular/core';
15
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
1✔
16
import { isValid } from 'date-fns';
17
import { InputBoolean, reqAnimFrame } from 'ngx-tethys/core';
×
18
import { TinyDate } from 'ngx-tethys/util';
×
19
import { ThyButtonComponent } from 'ngx-tethys/button';
×
20
import { NgIf, NgFor, DecimalPipe } from '@angular/common';
×
21

×
22
/**
23
 * 时间选择面板组件
24
 * @name thy-time-picker-panel
×
25
 */
×
26
@Component({
×
27
    selector: 'thy-time-picker-panel',
28
    templateUrl: './time-picker-panel.component.html',
×
29
    changeDetection: ChangeDetectionStrategy.OnPush,
×
30
    providers: [
31
        {
32
            provide: NG_VALUE_ACCESSOR,
×
33
            multi: true,
×
34
            useExisting: forwardRef(() => ThyTimePanelComponent)
×
35
        }
×
36
    ],
×
37
    host: {
×
38
        class: 'thy-time-picker-panel',
×
39
        '[class.thy-time-picker-panel-has-bottom-operation]': `thyShowOperations`,
×
40
        '[class.thy-time-picker-panel-columns-2]': `showColumnCount === 2`,
×
41
        '[class.thy-time-picker-panel-columns-3]': `showColumnCount === 3`
42
    },
×
43
    standalone: true,
×
44
    imports: [NgIf, NgFor, ThyButtonComponent, DecimalPipe]
×
45
})
×
46
export class ThyTimePanelComponent implements OnInit, OnDestroy, ControlValueAccessor {
×
47
    @ViewChild('hourListElement', { static: false }) hourListRef: ElementRef<HTMLElement>;
×
48

×
49
    @ViewChild('minuteListElement', { static: false }) minuteListRef: ElementRef<HTMLElement>;
×
50

×
51
    @ViewChild('secondListElement', { static: false }) secondListRef: ElementRef<HTMLElement>;
×
52

×
53
    /**
×
54
     * 展示的日期格式,支持 'HH:mm:ss' | 'HH:mm' | 'mm:ss'
55
     * @type string
56
     * @default HH:mm:ss
×
57
     */
×
58
    @Input() set thyFormat(value: string) {
×
59
        if (value) {
×
60
            const formatSet = new Set(value);
61
            this.showHourColumn = formatSet.has('H') || formatSet.has('h');
62
            this.showMinuteColumn = formatSet.has('m');
63
            this.showSecondColumn = formatSet.has('s');
×
64
        } else {
×
65
            this.showHourColumn = true;
×
66
            this.showMinuteColumn = true;
67
            this.showSecondColumn = true;
68
        }
×
69
        this.showColumnCount = [this.showHourColumn, this.showMinuteColumn, this.showSecondColumn].filter(m => m).length;
×
70
        this.cdr.markForCheck();
×
71
    }
×
72

73
    /**
74
     * 小时间隔步长
×
75
     * @type number
×
76
     */
×
77
    @Input() thyHourStep: number = 1;
×
78

79
    /**
80
     * 分钟间隔步长
×
81
     * @type number
×
82
     */
×
83
    @Input() thyMinuteStep: number = 1;
×
84

85
    /**
86
     * 秒间隔步长
×
87
     * @type number
×
88
     */
×
89
    @Input() thySecondStep: number = 1;
×
90

91
    /**
92
     * 展示选择此刻
×
93
     * @type boolean
×
94
     */
95
    @Input() @InputBoolean() thyShowSelectNow = true;
×
96

×
97
    /**
×
98
     * 展示底部操作
99
     * @type boolean
100
     */
×
101
    @Input() @InputBoolean() thyShowOperations = true;
×
102

×
103
    /**
104
     * 选择时间触发的事件
105
     * @type EventEmitter<Date>
×
106
     */
107
    @Output() thyPickChange = new EventEmitter<Date>();
×
108

×
109
    /**
110
     * 关闭面板事件
111
     * @type EventEmitter<void>
×
112
     */
113
    @Output() thyClosePanel = new EventEmitter<void>();
114

×
115
    // margin-top + 1px border
116
    SCROLL_OFFSET_SPACING = 5;
117

×
118
    SCROLL_DEFAULT_DURATION = 120;
119

120
    prefixCls = 'thy-time-picker-panel';
×
121

×
122
    hourRange: ReadonlyArray<{ value: number; disabled: boolean }> = [];
×
123

×
124
    minuteRange: ReadonlyArray<{ value: number; disabled: boolean }> = [];
125

×
126
    secondRange: ReadonlyArray<{ value: number; disabled: boolean }> = [];
×
127

×
128
    showHourColumn = true;
×
129

130
    showMinuteColumn = true;
131

132
    showSecondColumn = true;
133

134
    showColumnCount: number = 3;
135

×
136
    value: Date;
×
137

×
138
    hour: number;
139

140
    minute: number;
×
141

×
142
    second: number;
143

×
144
    initialScrollPosition: boolean;
×
145

146
    onValueChangeFn: (val: Date) => void = () => void 0;
×
147

×
148
    onTouchedFn: () => void = () => void 0;
149

×
150
    constructor(private cdr: ChangeDetectorRef, private ngZone: NgZone) {}
151

×
152
    ngOnInit(): void {
×
153
        this.generateTimeRange();
×
154
        this.initialValue();
×
155
        setTimeout(() => {
156
            this.initialScrollPosition = true;
×
157
        });
×
158
    }
×
159

×
160
    generateTimeRange() {
×
161
        this.hourRange = this.buildTimeRange(24, this.thyHourStep);
×
162
        this.minuteRange = this.buildTimeRange(60, this.thyMinuteStep);
×
163
        this.secondRange = this.buildTimeRange(60, this.thySecondStep);
164
    }
×
165

166
    pickHours(hours: { value: number; disabled: boolean }, index: number) {
167
        this.value.setHours(hours.value);
168
        this.hour = hours.value;
×
169
        this.thyPickChange.emit(this.value);
×
170
        this.scrollTo(this.hourListRef.nativeElement, index);
×
171
    }
172

×
173
    pickMinutes(minutes: { value: number; disabled: boolean }, index: number) {
×
174
        this.value.setMinutes(minutes.value);
175
        this.minute = minutes.value;
×
176
        this.thyPickChange.emit(this.value);
×
177
        this.scrollTo(this.minuteListRef.nativeElement, index);
178
    }
179

180
    pickSeconds(seconds: { value: number; disabled: boolean }, index: number) {
×
181
        this.value.setSeconds(seconds.value);
182
        this.second = seconds.value;
1✔
183
        this.thyPickChange.emit(this.value);
184
        this.scrollTo(this.secondListRef.nativeElement, index);
185
    }
186

1✔
187
    selectNow() {
188
        this.value = new Date();
189
        this.setHMSProperty();
190
        this.thyPickChange.emit(this.value);
191
        this.thyClosePanel.emit();
192
    }
193

194
    confirmPickTime() {
195
        this.onValueChangeFn(this.value || new Date());
196
        this.thyClosePanel.emit();
197
    }
198

199
    scrollTo(container: HTMLElement, index: number = 0, duration: number = this.SCROLL_DEFAULT_DURATION) {
200
        const offsetTop = (container.children[index] as HTMLElement).offsetTop - this.SCROLL_OFFSET_SPACING;
1✔
201
        this.runScrollAnimationFrame(container, offsetTop, duration);
202
    }
203

204
    writeValue(value: Date | number): void {
1✔
205
        if (value && isValid(value)) {
206
            this.value = new Date(value);
207
            this.setHMSProperty();
208
        } else {
1✔
209
            this.initialValue();
210
        }
211
        this.autoScroll(this.initialScrollPosition ? this.SCROLL_DEFAULT_DURATION : 0);
212

213
        this.cdr.markForCheck();
214
    }
215

216
    registerOnChange(fn: (value: Date) => void): void {
217
        this.onValueChangeFn = fn;
×
218
    }
219

220
    registerOnTouched(fn: () => void): void {
221
        this.onTouchedFn = fn;
222
    }
223

224
    trackByFn(index: number): number {
225
        return index;
226
    }
227

228
    private initialValue() {
229
        this.hour = 0;
230
        this.minute = 0;
231
        this.second = 0;
232
        this.value = new TinyDate().setHms(0, 0, 0).nativeDate;
233
    }
234

235
    private buildTimeRange(length: number, step: number = 1, start: number = 0, disables: number[] = []) {
236
        return new Array(Math.ceil(length / step)).fill(0).map((_, i) => {
237
            const value = (i + start) * step;
238
            return {
239
                value: value,
240
                disabled: disables.indexOf(value) > -1
241
            };
242
        });
243
    }
244

245
    private setHMSProperty() {
246
        this.hour = this.value.getHours();
247
        this.minute = this.value.getMinutes();
248
        this.second = this.value.getSeconds();
249
    }
250

251
    private resetScrollPosition() {
252
        if (this.hourListRef) {
253
            this.hourListRef.nativeElement.scrollTop = 0;
254
        }
255
        if (this.minuteListRef) {
256
            this.minuteListRef.nativeElement.scrollTop = 0;
257
        }
258
        if (this.secondListRef) {
259
            this.secondListRef.nativeElement.scrollTop = 0;
260
        }
261
        this.initialScrollPosition = false;
262
    }
263

264
    private runScrollAnimationFrame(container: HTMLElement, to: number, duration: number = this.SCROLL_DEFAULT_DURATION) {
265
        if (duration <= 0) {
266
            container.scrollTop = to;
267
            return;
268
        }
269
        const offset = to - container.scrollTop;
270
        const frame = (offset / duration) * 10;
271
        this.ngZone.runOutsideAngular(() => {
272
            reqAnimFrame(() => {
273
                container.scrollTop += frame;
274
                if (container.scrollTop === to) {
275
                    return;
276
                }
277
                this.runScrollAnimationFrame(container, to, duration - 10);
278
            });
279
        });
280
    }
281

282
    private autoScroll(duration: number = this.SCROLL_DEFAULT_DURATION) {
283
        if (this.hourListRef) {
284
            this.scrollTo(
285
                this.hourListRef.nativeElement,
286
                this.hourRange.findIndex(m => m.value === this.hour),
287
                duration
288
            );
289
        }
290
        if (this.minuteListRef) {
291
            this.scrollTo(
292
                this.minuteListRef.nativeElement,
293
                this.minuteRange.findIndex(m => m.value === this.minute),
294
                duration
295
            );
296
        }
297
        if (this.secondListRef) {
298
            this.scrollTo(
299
                this.secondListRef.nativeElement,
300
                this.secondRange.findIndex(m => m.value === this.second),
301
                duration
302
            );
303
        }
304
    }
305

306
    ngOnDestroy(): void {
307
        this.resetScrollPosition();
308
    }
309
}
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