timepicker-ui 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,11 +8,12 @@ Modern time picker library built with TypeScript. Works with any framework or va
8
8
 
9
9
  [Live Demo](https://timepicker-ui.vercel.app/) • [Documentation](https://timepicker-ui.vercel.app/docs) • [Changelog](./CHANGELOG.md)
10
10
 
11
- **Upgrading from v2?** Check the [upgrade guide](#upgrade-guide-v2--v3) below.
11
+ **Upgrading from v3?** Check the [upgrade guide](#upgrade-guide-v3--v4) below.
12
+ **Upgrading from v2?** Check the [v2 → v3 upgrade guide](#upgrade-guide-v2--v3).
12
13
 
13
14
  ## Features
14
15
 
15
- - 11 built-in themes (Material, Crane, Dark, Glassmorphic, Cyberpunk, and more)
16
+ - 10 built-in themes (Material, Crane, Dark, Glassmorphic, Cyberpunk, and more)
16
17
  - Mobile-first design with touch support
17
18
  - Framework agnostic - works with React, Vue, Angular, Svelte, or vanilla JS
18
19
  - Full TypeScript support
@@ -25,10 +26,9 @@ Modern time picker library built with TypeScript. Works with any framework or va
25
26
 
26
27
  This project is actively maintained. Some areas planned for improvement:
27
28
 
28
- - No unit/integration tests yet
29
- - Some files need refactoring
30
- - A few `any` types remain in the codebase
31
- - No performance monitoring
29
+ - Unit/integration test coverage could be expanded
30
+ - Performance monitoring tooling
31
+ - Further TypeScript strictness improvements
32
32
 
33
33
  Contributions welcome! Feel free to [open an issue or PR](https://github.com/pglejzer/timepicker-ui/issues).
34
34
 
@@ -73,12 +73,18 @@ picker.create();
73
73
 
74
74
  ```javascript
75
75
  const picker = new TimepickerUI(input, {
76
- theme: "dark",
77
- clockType: "24h",
78
- animation: true,
79
- backdrop: true,
80
- onConfirm: (data) => {
81
- console.log("Selected time:", data);
76
+ ui: {
77
+ theme: "dark",
78
+ animation: true,
79
+ backdrop: true,
80
+ },
81
+ clock: {
82
+ type: "24h",
83
+ },
84
+ callbacks: {
85
+ onConfirm: (data) => {
86
+ console.log("Selected time:", data);
87
+ },
82
88
  },
83
89
  });
84
90
  picker.create();
@@ -97,8 +103,9 @@ function TimePickerComponent() {
97
103
  useEffect(() => {
98
104
  if (inputRef.current) {
99
105
  const picker = new TimepickerUI(inputRef.current, {
100
- onConfirm: (data) => {
101
- console.log("Time selected:", data);
106
+ callbacks: {
107
+ onConfirm: (data) => {
108
+ console.log("Time selected:", data);
102
109
  },
103
110
  });
104
111
  picker.create();
@@ -115,49 +122,161 @@ function TimePickerComponent() {
115
122
 
116
123
  Full documentation available at [timepicker-ui.vercel.app/docs](https://timepicker-ui.vercel.app/docs)
117
124
 
118
- ### Key Options
119
-
120
- | Option | Type | Default | Description |
121
- | --------------------------- | -------------- | -------------- | --------------------------------------- |
122
- | `clockType` | `12h` / `24h` | `12h` | Clock format |
123
- | `theme` | string | `basic` | UI theme (11 themes available) |
124
- | `animation` | boolean | `true` | Enable animations |
125
- | `backdrop` | boolean | `true` | Show backdrop overlay |
126
- | `editable` | boolean | `false` | Allow manual input editing |
127
- | `mobile` | boolean | `false` | Force mobile version |
128
- | `disabledTime` | object | `undefined` | Disable specific hours/minutes |
129
- | `incrementHours` | number | `1` | Hour increment step |
130
- | `incrementMinutes` | number | `1` | Minute increment step |
131
- | `autoSwitchToMinutes` | boolean | `true` | Auto-switch to minutes after hour |
132
- | `amLabel` | string | `AM` | Custom AM label text |
133
- | `pmLabel` | string | `PM` | Custom PM label text |
134
- | `okLabel` | string | `OK` | OK button text |
135
- | `cancelLabel` | string | `Cancel` | Cancel button text |
136
- | `timeLabel` | string | `Select time` | Time label on desktop version |
137
- | `mobileTimeLabel` | string | `Enter Time` | Time label on mobile version |
138
- | `hourMobileLabel` | string | `Hour` | Hour label on mobile version |
139
- | `minuteMobileLabel` | string | `Minute` | Minute label on mobile version |
140
- | `appendModalSelector` | string | `""` | Append timepicker to custom selector |
141
- | `enableScrollbar` | boolean | `false` | Enable scroll if timepicker is open |
142
- | `enableSwitchIcon` | boolean | `false` | Show icon to switch desktop/mobile |
143
- | `focusInputAfterCloseModal` | boolean | `false` | Focus input after close modal |
144
- | `iconTemplate` | string | keyboard SVG | Custom template for desktop switch icon |
145
- | `iconTemplateMobile` | string | schedule SVG | Custom template for mobile switch icon |
146
- | `currentTime` | boolean/object | `undefined` | Set current time to input/picker |
147
- | `focusTrap` | boolean | `true` | Focus trap to modal elements |
148
- | `delayHandler` | number | `300` | Delay for clickable elements (ms) |
149
- | `id` | string | auto-generated | Custom ID for timepicker instance |
150
- | `inline` | object | `undefined` | Inline mode configuration |
151
- | `cssClass` | string | `undefined` | Additional custom CSS class |
152
- | `onConfirm` | function | `undefined` | Callback when time is confirmed |
153
- | `onCancel` | function | `undefined` | Callback when cancelled |
154
- | `onOpen` | function | `undefined` | Callback when picker opens |
155
- | `onUpdate` | function | `undefined` | Callback when time is updated |
156
- | `onSelectHour` | function | `undefined` | Callback when hour is selected |
157
- | `onSelectMinute` | function | `undefined` | Callback when minute is selected |
158
- | `onSelectAM` | function | `undefined` | Callback when AM is selected |
159
- | `onSelectPM` | function | `undefined` | Callback when PM is selected |
160
- | `onError` | function | `undefined` | Callback when error occurs |
125
+ ### Options Structure (v4.0.0 Breaking Change)
126
+
127
+ **Options are now organized into 5 logical groups:**
128
+
129
+ ```typescript
130
+ const picker = new TimepickerUI(input, {
131
+ clock: ClockOptions, // Clock behavior (type, increments, disabled time)
132
+ ui: UIOptions, // Appearance (theme, animation, mobile, inline)
133
+ labels: LabelsOptions, // Text labels (AM/PM, buttons, headers)
134
+ behavior: BehaviorOptions, // Behavior (focus, delays, ID)
135
+ callbacks: CallbacksOptions, // Event handlers
136
+ });
137
+ ```
138
+
139
+ ### Clock Options
140
+
141
+ | Property | Type | Default | Description |
142
+ | --------------------- | -------------- | ----------- | ------------------------------- |
143
+ | `type` | `12h` / `24h` | `12h` | Clock format |
144
+ | `incrementHours` | number | `1` | Hour increment step |
145
+ | `incrementMinutes` | number | `1` | Minute increment step |
146
+ | `autoSwitchToMinutes` | boolean | `false` | Auto-switch after hour selected |
147
+ | `disabledTime` | object | `undefined` | Disable specific hours/minutes |
148
+ | `currentTime` | boolean/object | `undefined` | Set current time to input |
149
+
150
+ ### UI Options
151
+
152
+ | Property | Type | Default | Description |
153
+ | --------------------- | ------- | ----------- | ------------------------------- |
154
+ | `theme` | string | `basic` | Theme (11 themes available) |
155
+ | `animation` | boolean | `true` | Enable animations |
156
+ | `backdrop` | boolean | `true` | Show backdrop overlay |
157
+ | `mobile` | boolean | `false` | Force mobile version |
158
+ | `enableSwitchIcon` | boolean | `false` | Show desktop/mobile switch icon |
159
+ | `editable` | boolean | `false` | Allow manual input editing |
160
+ | `enableScrollbar` | boolean | `false` | Enable scroll when picker open |
161
+ | `cssClass` | string | `undefined` | Additional CSS class |
162
+ | `appendModalSelector` | string | `""` | Custom container selector |
163
+ | `iconTemplate` | string | SVG | Desktop switch icon template |
164
+ | `iconTemplateMobile` | string | SVG | Mobile switch icon template |
165
+ | `inline` | object | `undefined` | Inline mode configuration |
166
+
167
+ ### Labels Options
168
+
169
+ | Property | Type | Default | Description |
170
+ | -------------- | ------ | ------------- | ------------------- |
171
+ | `am` | string | `AM` | AM label text |
172
+ | `pm` | string | `PM` | PM label text |
173
+ | `ok` | string | `OK` | OK button text |
174
+ | `cancel` | string | `Cancel` | Cancel button text |
175
+ | `time` | string | `Select time` | Desktop time label |
176
+ | `mobileTime` | string | `Enter Time` | Mobile time label |
177
+ | `mobileHour` | string | `Hour` | Mobile hour label |
178
+ | `mobileMinute` | string | `Minute` | Mobile minute label |
179
+
180
+ ### Behavior Options
181
+
182
+ | Property | Type | Default | Description |
183
+ | ---------------------- | ------- | -------------- | ----------------------- |
184
+ | `focusInputAfterClose` | boolean | `false` | Focus input after close |
185
+ | `focusTrap` | boolean | `true` | Trap focus in modal |
186
+ | `delayHandler` | number | `300` | Click delay (ms) |
187
+ | `id` | string | auto-generated | Custom instance ID |
188
+
189
+ ### Callbacks Options
190
+
191
+ | Property | Type | Description |
192
+ | ---------------- | -------- | ------------------------ |
193
+ | `onConfirm` | function | Time confirmed |
194
+ | `onCancel` | function | Cancelled |
195
+ | `onOpen` | function | Picker opened |
196
+ | `onUpdate` | function | Time updated (real-time) |
197
+ | `onSelectHour` | function | Hour selected |
198
+ | `onSelectMinute` | function | Minute selected |
199
+ | `onSelectAM` | function | AM selected |
200
+ | `onSelectPM` | function | PM selected |
201
+ | `onError` | function | Error occurred |
202
+
203
+ ### Migration from v3.x to v4.0.0
204
+
205
+ **All options must be moved into groups:**
206
+
207
+ ```diff
208
+ // v3.x (DEPRECATED)
209
+ -const picker = new TimepickerUI(input, {
210
+ - clockType: '24h',
211
+ - theme: 'dark',
212
+ - animation: true,
213
+ - incrementMinutes: 5,
214
+ - amLabel: 'AM',
215
+ - onConfirm: (data) => {}
216
+ -});
217
+
218
+ // v4.0.0 (NEW)
219
+ +const picker = new TimepickerUI(input, {
220
+ + clock: {
221
+ + type: '24h',
222
+ + incrementMinutes: 5
223
+ + },
224
+ + ui: {
225
+ + theme: 'dark',
226
+ + animation: true
227
+ + },
228
+ + labels: {
229
+ + am: 'AM'
230
+ + },
231
+ + callbacks: {
232
+ + onConfirm: (data) => {}
233
+ + }
234
+ +});
235
+ ```
236
+
237
+ **Full migration table:**
238
+
239
+ | v3.x (flat) | v4.0.0 (grouped) |
240
+ | --------------------------- | ------------------------------- |
241
+ | `clockType` | `clock.type` |
242
+ | `incrementHours` | `clock.incrementHours` |
243
+ | `incrementMinutes` | `clock.incrementMinutes` |
244
+ | `autoSwitchToMinutes` | `clock.autoSwitchToMinutes` |
245
+ | `disabledTime` | `clock.disabledTime` |
246
+ | `currentTime` | `clock.currentTime` |
247
+ | `theme` | `ui.theme` |
248
+ | `animation` | `ui.animation` |
249
+ | `backdrop` | `ui.backdrop` |
250
+ | `mobile` | `ui.mobile` |
251
+ | `enableSwitchIcon` | `ui.enableSwitchIcon` |
252
+ | `editable` | `ui.editable` |
253
+ | `enableScrollbar` | `ui.enableScrollbar` |
254
+ | `cssClass` | `ui.cssClass` |
255
+ | `appendModalSelector` | `ui.appendModalSelector` |
256
+ | `iconTemplate` | `ui.iconTemplate` |
257
+ | `iconTemplateMobile` | `ui.iconTemplateMobile` |
258
+ | `inline` | `ui.inline` |
259
+ | `amLabel` | `labels.am` |
260
+ | `pmLabel` | `labels.pm` |
261
+ | `okLabel` | `labels.ok` |
262
+ | `cancelLabel` | `labels.cancel` |
263
+ | `timeLabel` | `labels.time` |
264
+ | `mobileTimeLabel` | `labels.mobileTime` |
265
+ | `hourMobileLabel` | `labels.mobileHour` |
266
+ | `minuteMobileLabel` | `labels.mobileMinute` |
267
+ | `focusInputAfterCloseModal` | `behavior.focusInputAfterClose` |
268
+ | `focusTrap` | `behavior.focusTrap` |
269
+ | `delayHandler` | `behavior.delayHandler` |
270
+ | `id` | `behavior.id` |
271
+ | `onConfirm` | `callbacks.onConfirm` |
272
+ | `onCancel` | `callbacks.onCancel` |
273
+ | `onOpen` | `callbacks.onOpen` |
274
+ | `onUpdate` | `callbacks.onUpdate` |
275
+ | `onSelectHour` | `callbacks.onSelectHour` |
276
+ | `onSelectMinute` | `callbacks.onSelectMinute` |
277
+ | `onSelectAM` | `callbacks.onSelectAM` |
278
+ | `onSelectPM` | `callbacks.onSelectPM` |
279
+ | `onError` | `callbacks.onError` |
161
280
 
162
281
  ### Themes
163
282
 
@@ -168,7 +287,9 @@ import "timepicker-ui/main.css"; // Required base styles
168
287
  import "timepicker-ui/theme-dark.css"; // Specific theme
169
288
 
170
289
  const picker = new TimepickerUI(input, {
171
- theme: "dark",
290
+ ui: {
291
+ theme: "dark",
292
+ },
172
293
  });
173
294
  ```
174
295
 
@@ -176,10 +297,12 @@ const picker = new TimepickerUI(input, {
176
297
 
177
298
  ```javascript
178
299
  const picker = new TimepickerUI(input, {
179
- disabledTime: {
180
- hours: [1, 3, 5, 8],
181
- minutes: [15, 30, 45],
182
- interval: "10:00 AM - 2:00 PM",
300
+ clock: {
301
+ disabledTime: {
302
+ hours: [1, 3, 5, 8],
303
+ minutes: [15, 30, 45],
304
+ interval: "10:00 AM - 2:00 PM",
305
+ },
183
306
  },
184
307
  });
185
308
  ```
@@ -188,11 +311,13 @@ const picker = new TimepickerUI(input, {
188
311
 
189
312
  ```javascript
190
313
  const picker = new TimepickerUI(input, {
191
- inline: {
192
- enabled: true,
193
- containerId: "timepicker-container",
194
- showButtons: false,
195
- autoUpdate: true,
314
+ ui: {
315
+ inline: {
316
+ enabled: true,
317
+ containerId: "timepicker-container",
318
+ showButtons: false,
319
+ autoUpdate: true,
320
+ },
196
321
  },
197
322
  });
198
323
  ```
@@ -223,7 +348,7 @@ TimepickerUI.destroyAll(); // Destroy all instances
223
348
 
224
349
  ## Events
225
350
 
226
- Listen to timepicker events using the new **EventEmitter API** (recommended) or the legacy DOM events (deprecated, will be removed in v4):
351
+ Listen to timepicker events using the **EventEmitter API** (v4+) or callback options:
227
352
 
228
353
  ### EventEmitter API (Recommended)
229
354
 
@@ -274,21 +399,224 @@ picker.once("confirm", (data) => {
274
399
  picker.off("confirm", handler);
275
400
  ```
276
401
 
277
- ### Legacy DOM Events (Deprecated)
402
+ ### Legacy DOM Events (v3.x Only)
403
+
404
+ ⚠️ **Only Available in v3.x - Completely Removed in v4.0.0**
405
+
406
+ DOM events (e.g., `timepicker:confirm`) were removed in v4.0.0. Use the **EventEmitter API** shown above or **callback options** instead.
278
407
 
279
- **Note:** DOM events (e.g., `timepicker:confirm`) are deprecated and will be removed in v4. Please migrate to the new EventEmitter API shown above.
408
+ **v3.x code (no longer works in v4):**
280
409
 
281
410
  ```javascript
282
411
  input.addEventListener("timepicker:confirm", (e) => {
283
412
  console.log("Time:", e.detail);
284
413
  });
414
+ ```
415
+
416
+ **v4.0.0 alternatives:**
417
+
418
+ ```javascript
419
+ // Option 1: EventEmitter API
420
+ picker.on("confirm", (data) => {
421
+ console.log("Time:", data);
422
+ });
423
+
424
+ // Option 2: Callback options
425
+ const picker = new TimepickerUI(input, {
426
+ callbacks: {
427
+ onConfirm: (data) => {
428
+ console.log("Time:", data);
429
+ },
430
+ },
431
+ });
432
+ ```
433
+
434
+ Removed events: `timepicker:open`, `timepicker:cancel`, `timepicker:confirm`, `timepicker:update`, `timepicker:select-hour`, `timepicker:select-minute`, `timepicker:select-am`, `timepicker:select-pm`, `timepicker:error`
435
+
436
+ ## Upgrade Guide: v3 → v4
437
+
438
+ ### Breaking Changes
439
+
440
+ **1. CSS Class Names Renamed**
441
+
442
+ All CSS classes have been shortened from `timepicker-ui-*` to `tp-ui-*`:
443
+
444
+ ```css
445
+ /* v3 */
446
+ .timepicker-ui-wrapper {
447
+ }
448
+ .timepicker-ui-modal {
449
+ }
450
+ .timepicker-ui-clock-face {
451
+ }
452
+ .timepicker-ui-hour {
453
+ }
454
+ .timepicker-ui-minutes {
455
+ }
456
+ .timepicker-ui-am {
457
+ }
458
+ .timepicker-ui-pm {
459
+ }
460
+
461
+ /* v4 */
462
+ .tp-ui-wrapper {
463
+ }
464
+ .tp-ui-modal {
465
+ }
466
+ .tp-ui-clock-face {
467
+ }
468
+ .tp-ui-hour {
469
+ }
470
+ .tp-ui-minutes {
471
+ }
472
+ .tp-ui-am {
473
+ }
474
+ .tp-ui-pm {
475
+ }
476
+ ```
477
+
478
+ **Impact:** If you have custom CSS targeting timepicker elements, update all class selectors.
479
+
480
+ **2. Grouped Options Structure**
481
+
482
+ All options are now organized into logical groups for better maintainability:
483
+
484
+ ```javascript
485
+ // v3
486
+ const picker = new TimepickerUI(input, {
487
+ clockType: "12h",
488
+ theme: "dark",
489
+ enableSwitchIcon: true,
490
+ mobile: true,
491
+ animation: true,
492
+ backdrop: true,
493
+ focusTrap: true,
494
+ editable: true,
495
+ onConfirm: (data) => console.log(data),
496
+ });
497
+
498
+ // v4 - Grouped options
499
+ const picker = new TimepickerUI(input, {
500
+ clock: {
501
+ type: "12h",
502
+ incrementHours: 1,
503
+ incrementMinutes: 1,
504
+ currentTime: { time: new Date(), updateInput: true },
505
+ disabledTime: { hours: [1, 2, 3] },
506
+ autoSwitchToMinutes: false,
507
+ },
508
+ ui: {
509
+ theme: "dark",
510
+ enableSwitchIcon: true,
511
+ mobile: true,
512
+ animation: true,
513
+ backdrop: true,
514
+ editable: true,
515
+ cssClass: "my-custom-class",
516
+ inline: { enabled: false },
517
+ },
518
+ labels: {
519
+ time: "Select Time",
520
+ am: "AM",
521
+ pm: "PM",
522
+ ok: "OK",
523
+ cancel: "Cancel",
524
+ },
525
+ behavior: {
526
+ focusTrap: true,
527
+ focusInputAfterClose: false,
528
+ delayHandler: 300,
529
+ },
530
+ callbacks: {
531
+ onConfirm: (data) => console.log(data),
532
+ onCancel: (data) => console.log(data),
533
+ onOpen: (data) => console.log(data),
534
+ onUpdate: (data) => console.log(data),
535
+ onSelectHour: (data) => console.log(data),
536
+ onSelectMinute: (data) => console.log(data),
537
+ onSelectAM: (data) => console.log(data),
538
+ onSelectPM: (data) => console.log(data),
539
+ onError: (data) => console.log(data),
540
+ },
541
+ });
542
+ ```
543
+
544
+ **2. Legacy DOM Events Removed**
545
+
546
+ DOM events have been completely removed. Use EventEmitter API or callback options:
547
+
548
+ ```javascript
549
+ // v3 - Deprecated (removed in v4)
550
+ input.addEventListener("timepicker:confirm", (e) => {
551
+ console.log(e.detail);
552
+ });
285
553
 
286
- input.addEventListener("timepicker:cancel", (e) => {
287
- console.log("Cancelled");
554
+ // v4 - EventEmitter API (recommended)
555
+ picker.on("confirm", (data) => {
556
+ console.log(data);
557
+ });
558
+
559
+ // v4 - Or use callback options
560
+ const picker = new TimepickerUI(input, {
561
+ callbacks: {
562
+ onConfirm: (data) => console.log(data),
563
+ },
288
564
  });
289
565
  ```
290
566
 
291
- Available legacy events: `timepicker:open`, `timepicker:cancel`, `timepicker:confirm`, `timepicker:update`, `timepicker:select-hour`, `timepicker:select-minute`, `timepicker:select-am`, `timepicker:select-pm`, `timepicker:error`
567
+ **3. setTheme() Method Removed**
568
+
569
+ Programmatic theme setting via `setTheme()` has been removed. Use CSS classes instead:
570
+
571
+ ```javascript
572
+ // v3 - Removed in v4
573
+ picker.setTheme({
574
+ primaryColor: "#ff0000",
575
+ backgroundColor: "#000000",
576
+ });
577
+
578
+ // v4 - Use CSS classes with CSS variables
579
+ const picker = new TimepickerUI(input, {
580
+ ui: { cssClass: "my-custom-theme" },
581
+ });
582
+ ```
583
+
584
+ ```css
585
+ /* Define custom theme in CSS */
586
+ .tp-ui-wrapper.my-custom-theme {
587
+ --tp-primary: #ff0000;
588
+ --tp-bg: #000000;
589
+ --tp-surface: #f0f0f0;
590
+ }
591
+ ```
592
+
593
+ **4. SSR-Safe Architecture**
594
+
595
+ All modules are now SSR-safe and can be imported in Node.js environments without crashing.
596
+
597
+ ### Migration Steps
598
+
599
+ 1. **Update option structure** - Move options into `clock`, `ui`, `labels`, `behavior`, `callbacks` groups
600
+ 2. **Replace DOM events** - Switch to `picker.on()` EventEmitter API or callback options
601
+ 3. **Remove setTheme() calls** - Use CSS classes with CSS variable overrides
602
+ 4. **Test SSR compatibility** - If using Next.js/Nuxt/Remix, verify hydration works correctly
603
+
604
+ ### New in v4
605
+
606
+ - **Composition-based architecture** - No inheritance, pure composition with managers
607
+ - **EventEmitter API** - Type-safe event handling with `on()`, `off()`, `once()`
608
+ - **Callback bridge** - Callbacks automatically connected to EventEmitter
609
+ - **SSR compatibility** - All modules can be imported in Node.js
610
+ - **Better TypeScript types** - Fully typed event payloads and options
611
+ - **Smaller bundle** - Removed unused code, optimized build (63.3 KB ESM)
612
+ - **Focus improvements** - Auto-focus on modal open, auto-focus on minute switch
613
+
614
+ ### Bundle Size Comparison
615
+
616
+ - v3: 80 KB ESM
617
+ - v4: 63.3 KB ESM (-20.9%)
618
+
619
+ ---
292
620
 
293
621
  ## Upgrade Guide: v2 → v3
294
622