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

pglejzer / timepicker-ui / 27286160512

10 Jun 2026 03:12PM UTC coverage: 86.868% (+1.5%) from 85.325%
27286160512

push

github

web-flow
Merge pull request #167 from pglejzer/feature/4.4.0

Feature/4.4.0

2277 of 2878 branches covered (79.12%)

Branch coverage included in aggregate %.

406 of 427 new or added lines in 29 files covered. (95.08%)

162 existing lines in 27 files now uncovered.

4523 of 4950 relevant lines covered (91.37%)

115.32 hits per line

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

92.55
/app/src/managers/clock/ClockSystem.ts
1
import type { ClockType, ClockMode, DisabledTimeConfig, ClockState, RenderConfig } from './types';
2
import { ClockRenderer } from './renderer/ClockRenderer';
13✔
3
import { ClockController, type ClockControllerCallbacks } from './controller/ClockController';
13✔
4
import { DragHandlers, type DragHandlersConfig } from './handlers/DragHandlers';
13✔
5
import { HOURS_12, HOURS_24, MINUTES_STEP_5 } from '../../utils/template';
13✔
6

13✔
7
export interface ClockSystemConfig {
13✔
8
  clockFace: HTMLElement;
9
  tipsWrapper: HTMLElement;
10
  tipsWrapperFor24h?: HTMLElement;
77✔
11
  clockHand: HTMLElement;
77✔
12
  circle: HTMLElement;
77✔
13
  clockType: ClockType;
77✔
14
  disabledTime: DisabledTimeConfig | null;
77✔
15
  initialHour: string;
16
  initialMinute: string;
17
  initialAmPm: '' | 'AM' | 'PM';
18
  theme?: string;
19
  incrementHours?: number;
20
  incrementMinutes?: number;
21
  smoothHourSnap?: boolean;
22
  onHourChange?: (hour: string) => void;
77✔
23
  onMinuteChange?: (minute: string) => void;
77✔
24
  timepicker: unknown;
25
  dragConfig?: DragHandlersConfig;
26
}
27

28
export class ClockSystem {
29
  private renderer: ClockRenderer;
30
  private controller: ClockController;
31
  private dragHandlers: DragHandlers;
77✔
32
  private clockType: ClockType;
33
  private disabledTime: DisabledTimeConfig | null;
34
  private tipsWrapper: HTMLElement;
35
  private tipsWrapperFor24h?: HTMLElement;
77✔
36

77✔
37
  constructor(config: ClockSystemConfig) {
38
    this.clockType = config.clockType;
39
    this.disabledTime = config.disabledTime;
75✔
40
    this.tipsWrapper = config.tipsWrapper;
75✔
41
    this.tipsWrapperFor24h = config.tipsWrapperFor24h;
42

43
    const renderConfig: RenderConfig = {
80✔
44
      clockFace: config.clockFace,
80✔
45
      tipsWrapper: config.tipsWrapper,
20✔
46
      tipsWrapperFor24h: config.tipsWrapperFor24h,
47
      clockHand: config.clockHand,
80✔
48
      circle: config.circle,
80✔
49
      theme: config.theme,
80✔
50
    };
51

52
    this.renderer = new ClockRenderer(renderConfig);
7✔
53

7✔
54
    const initialState: ClockState = {
1✔
55
      hour: config.initialHour,
56
      minute: config.initialMinute,
7✔
57
      amPm: config.initialAmPm,
7✔
58
      hourAngle: this.calculateInitialAngle('hours', config.initialHour),
7✔
59
      minuteAngle: this.calculateInitialAngle('minutes', config.initialMinute),
60
      mode: 'hours',
61
    };
4✔
62

63
    const callbacks: ClockControllerCallbacks = {
64
      onHourChange: config.onHourChange,
4✔
65
      onMinuteChange: config.onMinuteChange,
66
    };
67

4✔
68
    this.controller = new ClockController(
4✔
69
      this.renderer,
4!
70
      initialState,
4✔
71
      config.clockType,
4✔
72
      config.disabledTime,
4✔
73
      config.incrementHours || 1,
74
      config.incrementMinutes || 1,
UNCOV
75
      config.smoothHourSnap ?? true,
×
76
      callbacks,
×
77
    );
×
78

79
    this.dragHandlers = new DragHandlers(this.controller, config.clockFace, config.dragConfig || {});
80
  }
81

4✔
82
  initialize(): void {
83
    this.dragHandlers.attach();
84
    this.switchToHours();
4✔
85
  }
86

87
  switchToHours(): void {
3✔
88
    this.controller.switchMode('hours');
89

90
    if (this.clockType === '24h' && this.tipsWrapperFor24h) {
2✔
91
      this.tipsWrapperFor24h.classList.remove('none');
2✔
92
    }
2✔
93

2!
94
    this.renderHourTips();
2✔
95

96
    const state = this.controller.getState();
UNCOV
97
    this.renderer.setActiveValue(state.hour);
×
98
  }
99

100
  switchToMinutes(): void {
101
    this.controller.switchMode('minutes');
86✔
102

86✔
103
    if (this.tipsWrapperFor24h) {
86✔
104
      this.tipsWrapperFor24h.classList.add('none');
20✔
105
    }
20!
106

20✔
107
    this.renderMinuteTips();
108

109
    const state = this.controller.getState();
110
    this.renderer.setActiveValue(state.minute);
66✔
111
  }
112

113
  setHour(value: string): void {
114
    this.controller.setValue('hours', value);
7✔
115
  }
7✔
116

7✔
117
  setMinute(value: string): void {
7✔
118
    this.controller.setValue('minutes', value);
119
  }
120

154✔
121
  setAmPm(amPm: 'AM' | 'PM'): void {
154✔
122
    this.controller.setAmPm(amPm);
77✔
123
    const state = this.controller.getState();
124
    if (state.mode === 'hours') {
125
      this.renderHourTips();
77✔
126
      this.renderer.setHandAngle(state.hourAngle);
127
      this.renderer.setActiveValue(state.hour);
128
    } else {
129
      this.renderMinuteTips();
62✔
130
      this.renderer.setHandAngle(state.minuteAngle);
62✔
131
      this.renderer.setActiveValue(state.minute);
132
    }
133
  }
3✔
134

135
  getHour(): string {
136
    return this.controller.getHour();
2✔
137
  }
138

139
  getMinute(): string {
13✔
140
    return this.controller.getMinute();
141
  }
142

143
  getAmPm(): string {
144
    return this.controller.getAmPm();
145
  }
146

147
  updateDisabledTime(disabledTime: DisabledTimeConfig | null): void {
148
    this.disabledTime = disabledTime;
149
    this.controller.updateDisabledTime(disabledTime);
150
    const state = this.controller.getState();
151

152
    if (state.mode === 'hours') {
153
      this.renderHourTips();
154
    } else {
155
      this.renderMinuteTips();
156
    }
157
  }
158

159
  private renderHourTips(): void {
160
    const state = this.controller.getState();
161
    const amPm = state.amPm;
162

163
    if (this.clockType === '24h') {
164
      this.renderer.renderTips(
165
        HOURS_12,
166
        'tp-ui-hour-time-12',
167
        'hours',
168
        this.disabledTime,
169
        this.clockType,
170
        true,
171
        this.tipsWrapper,
172
        amPm,
173
      );
174

175
      if (this.tipsWrapperFor24h) {
176
        this.renderer.renderTips(
177
          HOURS_24,
178
          'tp-ui-hour-time-24',
179
          'hours',
180
          this.disabledTime,
181
          this.clockType,
182
          true,
183
          this.tipsWrapperFor24h,
184
          amPm,
185
        );
186
      }
187
    } else {
188
      this.renderer.renderTips(
189
        HOURS_12,
190
        'tp-ui-hour-time-12',
191
        'hours',
192
        this.disabledTime,
193
        this.clockType,
194
        true,
195
        undefined,
196
        amPm,
197
      );
198
    }
199
  }
200

201
  private renderMinuteTips(): void {
202
    const state = this.controller.getState();
203
    const amPm = state.amPm;
204
    const currentHour = state.hour;
205

206
    this.renderer.renderTips(
207
      MINUTES_STEP_5,
208
      'tp-ui-minutes-time',
209
      'minutes',
210
      this.disabledTime,
211
      this.clockType,
212
      true,
213
      undefined,
214
      amPm,
215
      currentHour,
216
    );
217
  }
218

219
  private calculateInitialAngle(mode: ClockMode, value: string): number {
220
    const numValue = parseInt(value, 10);
221

222
    if (mode === 'hours') {
223
      return (numValue % 12) * 30;
224
    } else {
225
      return numValue * 6;
226
    }
227
  }
228

229
  destroy(): void {
230
    this.dragHandlers.detach();
231
    this.controller.destroy();
232
  }
233

234
  blockInteractions(): void {
235
    this.dragHandlers.block();
236
  }
237

238
  unblockInteractions(): void {
239
    this.dragHandlers.unblock();
240
  }
241
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc