timepicker-ui 3.0.1 → 3.1.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
@@ -1,62 +1,46 @@
1
1
  # timepicker-ui
2
2
 
3
- > ⚠️ **Upgrading from v2 to v3?**
4
- > Major changes were introduced in version 3.0.
5
- > 👉 [Click here to view the Upgrade Guide](#📈-upgrade-guide-v2-→-v3)
6
-
7
- A modern, lightweight, and fully customizable time picker library built with TypeScript. Features Google's Material Design principles with extensive theming support and framework-agnostic architecture.
3
+ Modern time picker library built with TypeScript. Works with any framework or vanilla JavaScript.
8
4
 
9
5
  [![npm version](https://badge.fury.io/js/timepicker-ui.svg)](https://badge.fury.io/js/timepicker-ui)
10
6
  [![downloads](https://img.shields.io/npm/dw/timepicker-ui)](https://npmcharts.com/compare/timepicker-ui?minimal=true)
11
7
  [![license](https://img.shields.io/badge/license-MIT-green.svg)](https://img.shields.io/npm/l/timepicker-ui)
12
8
 
13
- ---
14
-
15
- ## 📦 Live Demo
16
-
17
- Curious how it works in practice?
18
- 👉 [Click here to see live examples](https://timepicker-ui.vercel.app/)
9
+ [Live Demo](https://timepicker-ui.vercel.app/) • [Documentation](https://timepicker-ui.vercel.app/docs) • [Changelog](./CHANGELOG.md)
19
10
 
20
- ---
11
+ **Upgrading from v2?** Check the [upgrade guide](#upgrade-guide-v2--v3) below.
21
12
 
22
- ## Features
13
+ ## Features
23
14
 
24
- - 🎨 **9 Built-in Themes** Material, Crane, Dark, Glassmorphic, Cyberpunk, AI, and more
25
- - 📱 **Mobile-First Design** — Responsive with touch and keyboard support
26
- - 🚀 **Framework Agnostic** Works with vanilla JS, React, Vue, Angular, and others
27
- - 🔧 **TypeScript Support** — Full type definitions and IntelliSense
28
- - 🎯 **Inline Mode** Always-visible timepicker without modal overlay
29
- - 🛠️ **Rich API** — Comprehensive methods and event system
30
- - **Accessible** — ARIA-compliant with keyboard navigation
31
- - 🌐 **SSR Compatible** — Works with Next.js, Nuxt, and other SSR frameworks
32
- - 📦 **Lightweight** — Minimal footprint with tree-shaking support
15
+ - 9 built-in themes (Material, Crane, Dark, Glassmorphic, Cyberpunk, and more)
16
+ - Mobile-first design with touch support
17
+ - Framework agnostic - works with React, Vue, Angular, Svelte, or vanilla JS
18
+ - Full TypeScript support
19
+ - Inline mode for always-visible timepicker
20
+ - ARIA-compliant and keyboard accessible
21
+ - SSR compatible
22
+ - Lightweight with tree-shaking support
33
23
 
34
- ## 🧭 Roadmap & Known Limitations
24
+ ## Known Limitations
35
25
 
36
- This project is actively maintained and evolving. Some areas are planned for future improvements:
26
+ This project is actively maintained. Some areas planned for improvement:
37
27
 
38
- - No formal tests (unit/integration) **planned for future releases**
39
- - Some files are too large — **will be split/refactored**
40
- - A few `any` types in the codebase — **will be replaced with strict typings**
41
- - No performance monitoring — **planned metrics/logging in dev mode**
28
+ - No unit/integration tests yet
29
+ - Some files need refactoring
30
+ - A few `any` types remain in the codebase
31
+ - No performance monitoring
42
32
 
43
- If you're interested in contributing to any of these areas, feel free to [open an issue or pull request](https://github.com/pglejzer/timepicker-ui/issues)!
33
+ Contributions welcome! Feel free to [open an issue or PR](https://github.com/pglejzer/timepicker-ui/issues).
44
34
 
45
- ---
46
-
47
- ## 🚀 Installation
35
+ ## Installation
48
36
 
49
37
  ```bash
50
38
  npm install timepicker-ui
51
- # or
52
- yarn add timepicker-ui
53
39
  ```
54
40
 
55
- ---
56
-
57
- ## 📐 Global CSS Required
41
+ ## Important: Global CSS Required
58
42
 
59
- For correct styling, make sure your app includes this global CSS rule:
43
+ Your app needs this global CSS rule for correct styling:
60
44
 
61
45
  ```css
62
46
  *,
@@ -66,11 +50,9 @@ For correct styling, make sure your app includes this global CSS rule:
66
50
  }
67
51
  ```
68
52
 
69
- This is a common default in most projects and is required by `timepicker-ui` to avoid layout issues.
53
+ Most projects include this by default.
70
54
 
71
- ---
72
-
73
- ## 📖 Quick Start
55
+ ## Quick Start
74
56
 
75
57
  ### Basic Usage
76
58
 
@@ -80,6 +62,7 @@ This is a common default in most projects and is required by `timepicker-ui` to
80
62
 
81
63
  ```javascript
82
64
  import { TimepickerUI } from "timepicker-ui";
65
+ import "timepicker-ui/main.css";
83
66
 
84
67
  const input = document.querySelector("#timepicker");
85
68
  const picker = new TimepickerUI(input);
@@ -94,15 +77,19 @@ const picker = new TimepickerUI(input, {
94
77
  clockType: "24h",
95
78
  animation: true,
96
79
  backdrop: true,
80
+ onConfirm: (data) => {
81
+ console.log("Selected time:", data);
82
+ },
97
83
  });
98
84
  picker.create();
99
85
  ```
100
86
 
101
- ### React Integration
87
+ ### React Example
102
88
 
103
89
  ```tsx
104
90
  import { useEffect, useRef } from "react";
105
91
  import { TimepickerUI } from "timepicker-ui";
92
+ import "timepicker-ui/main.css";
106
93
 
107
94
  function TimePickerComponent() {
108
95
  const inputRef = useRef<HTMLInputElement>(null);
@@ -124,565 +111,333 @@ function TimePickerComponent() {
124
111
  }
125
112
  ```
126
113
 
127
- ---
128
-
129
- ## ⚙️ Configuration Options
130
-
131
- | Option | Type | Default | Description |
132
- | -------------------------------- | ------------------- | --------------- | ---------------------------------------------------- |
133
- | `amLabel` | `string` | `"AM"` | Custom text for AM label |
134
- | `animation` | `boolean` | `true` | Enable/disable open/close animations |
135
- | `appendModalSelector` | `string` | `""` | DOM selector to append timepicker (defaults to body) |
136
- | `backdrop` | `boolean` | `true` | Show/hide backdrop overlay |
137
- | `cancelLabel` | `string` | `"CANCEL"` | Text for cancel button |
138
- | `clockType` | `"12h" \| "24h"` | `"12h"` | Clock format type |
139
- | `cssClass` | `string` | `undefined` | Additional CSS class for timepicker wrapper |
140
- | `currentTime` | `boolean \| object` | `undefined` | Set current time to input and picker |
141
- | `delayHandler` | `number` | `300` | Debounce delay for buttons (ms) |
142
- | `disabledTime` | `object` | `undefined` | Disable specific hours, minutes, or intervals |
143
- | `editable` | `boolean` | `false` | Allow manual input editing |
144
- | `enableScrollbar` | `boolean` | `false` | Keep page scroll when picker is open |
145
- | `enableSwitchIcon` | `boolean` | `false` | Show desktop/mobile switch icon |
146
- | `focusInputAfterCloseModal` | `boolean` | `false` | Focus input after closing modal |
147
- | `focusTrap` | `boolean` | `true` | Trap focus within modal |
148
- | `hourMobileLabel` | `string` | `"Hour"` | Hour label for mobile version |
149
- | `iconTemplate` | `string` | Material Icons | HTML template for desktop switch icon |
150
- | `iconTemplateMobile` | `string` | Material Icons | HTML template for mobile switch icon |
151
- | `id` | `string` | `undefined` | Custom ID for timepicker instance |
152
- | `incrementHours` | `number` | `1` | Hour increment step (1, 2, 3) |
153
- | `incrementMinutes` | `number` | `1` | Minute increment step (1, 5, 10, 15) |
154
- | `inline` | `object` | `undefined` | Inline mode configuration |
155
- | `minuteMobileLabel` | `string` | `"Minute"` | Minute label for mobile version |
156
- | `mobile` | `boolean` | `false` | Force mobile version |
157
- | `mobileTimeLabel` | `string` | `"Enter Time"` | Time label for mobile version |
158
- | `okLabel` | `string` | `"OK"` | Text for OK button |
159
- | `pmLabel` | `string` | `"PM"` | Custom text for PM label |
160
- | `switchToMinutesAfterSelectHour` | `boolean` | `true` | Auto-switch to minutes after hour selection |
161
- | `theme` | Theme | `"basic"` | UI theme (see themes section) |
162
- | `timeLabel` | `string` | `"Select Time"` | Time label for desktop version |
163
-
164
- ### Inline Mode Configuration
114
+ ## Configuration
115
+
116
+ Full documentation available at [timepicker-ui.vercel.app/docs](https://timepicker-ui.vercel.app/docs)
117
+
118
+ ### Key Options
119
+
120
+ | Option | Type | Default | Description |
121
+ | -------------------------------- | ------------- | ----------- | --------------------------------- |
122
+ | `clockType` | `12h` / `24h` | `12h` | Clock format |
123
+ | `theme` | string | `basic` | UI theme (9 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
+ | `switchToMinutesAfterSelectHour` | boolean | `true` | Auto-switch to minutes after hour |
132
+ | `okLabel` | string | `OK` | OK button text |
133
+ | `cancelLabel` | string | `CANCEL` | Cancel button text |
134
+ | `onConfirm` | function | `undefined` | Callback when time is confirmed |
135
+ | `onCancel` | function | `undefined` | Callback when cancelled |
136
+ | `onOpen` | function | `undefined` | Callback when picker opens |
137
+ | `onUpdate` | function | `undefined` | Callback when time is updated |
138
+ | `onSelectHour` | function | `undefined` | Callback when hour is selected |
139
+ | `onSelectMinute` | function | `undefined` | Callback when minute is selected |
140
+ | `onSelectAM` | function | `undefined` | Callback when AM is selected |
141
+ | `onSelectPM` | function | `undefined` | Callback when PM is selected |
142
+ | `onError` | function | `undefined` | Callback when error occurs |
143
+
144
+ ### Themes
145
+
146
+ Available themes: `basic`, `crane-straight`, `crane-radius`, `m3`, `dark`, `glassmorphic`, `pastel`, `ai`, `cyberpunk`
165
147
 
166
148
  ```javascript
149
+ import "timepicker-ui/main.css"; // Required base styles
150
+ import "timepicker-ui/theme-dark.css"; // Specific theme
151
+
167
152
  const picker = new TimepickerUI(input, {
168
- inline: {
169
- enabled: true,
170
- containerId: "timepicker-container",
171
- showButtons: false, // Hide OK/Cancel buttons
172
- autoUpdate: true, // Auto-update input on change
173
- },
153
+ theme: "dark",
174
154
  });
175
155
  ```
176
156
 
177
- ### Disabled Time Configuration
157
+ ### Disabled Time
178
158
 
179
159
  ```javascript
180
160
  const picker = new TimepickerUI(input, {
181
161
  disabledTime: {
182
- hours: [1, 3, 5, 8], // Disable specific hours
183
- minutes: [15, 30, 45], // Disable specific minutes
184
- interval: "10:00 AM - 2:00 PM", // Disable time range
162
+ hours: [1, 3, 5, 8],
163
+ minutes: [15, 30, 45],
164
+ interval: "10:00 AM - 2:00 PM",
185
165
  },
186
166
  });
187
167
  ```
188
168
 
189
- ---
190
-
191
- ## 🎨 Themes
192
-
193
- > **Note:** As of v3.0, you must import CSS styles manually. See [Upgrade Guide](#upgrade-guide-v2--v3) for details.
194
-
195
- Choose from 9 built-in themes:
196
-
197
- | Theme | Description |
198
- | ---------------- | -------------------------------------- |
199
- | `basic` | Default Material Design theme |
200
- | `crane-straight` | Google Crane theme with straight edges |
201
- | `crane-radius` | Google Crane theme with rounded edges |
202
- | `m3` | Material Design 3 (Material You) |
203
- | `dark` | Dark mode theme |
204
- | `glassmorphic` | Modern glass effect |
205
- | `pastel` | Soft pastel colors |
206
- | `ai` | Futuristic AI-inspired theme |
207
- | `cyberpunk` | Neon cyberpunk aesthetic |
208
-
209
- ```javascript
210
- const picker = new TimepickerUI(input, {
211
- theme: "cyberpunk",
212
- });
213
- ```
214
-
215
- ---
216
-
217
- ## 📞 Callbacks
218
-
219
- Configure callback functions to handle timepicker events:
220
-
221
- | Callback | Type | Description |
222
- | ---------------- | ---------------- | ---------------------------------------------- |
223
- | `onOpen` | `(data) => void` | Triggered when timepicker opens |
224
- | `onCancel` | `(data) => void` | Triggered when picker is cancelled |
225
- | `onConfirm` | `(data) => void` | Triggered when time is confirmed (OK clicked) |
226
- | `onUpdate` | `(data) => void` | Triggered during clock interaction (real-time) |
227
- | `onSelectHour` | `(data) => void` | Triggered when hour mode is activated |
228
- | `onSelectMinute` | `(data) => void` | Triggered when minute mode is activated |
229
- | `onSelectAM` | `(data) => void` | Triggered when AM is selected |
230
- | `onSelectPM` | `(data) => void` | Triggered when PM is selected |
231
- | `onError` | `(data) => void` | Triggered when invalid time format is detected |
232
-
233
- ### Callback Data Structure
234
-
235
- ```typescript
236
- interface CallbackData {
237
- hour?: string;
238
- minutes?: string;
239
- type?: string; // 'AM' or 'PM'
240
- degreesHours?: number;
241
- degreesMinutes?: number;
242
- error?: string; // Only for onError
243
- // ... additional context data
244
- }
245
- ```
246
-
247
- ### Example Usage
169
+ ### Inline Mode
248
170
 
249
171
  ```javascript
250
172
  const picker = new TimepickerUI(input, {
251
- onConfirm: (data) => {
252
- console.log(`Time selected: ${data.hour}:${data.minutes} ${data.type}`);
253
- },
254
- onCancel: (data) => {
255
- console.log("User cancelled time selection");
256
- },
257
- onError: (data) => {
258
- alert(`Invalid time format: ${data.error}`);
173
+ inline: {
174
+ enabled: true,
175
+ containerId: "timepicker-container",
176
+ showButtons: false,
177
+ autoUpdate: true,
259
178
  },
260
179
  });
261
180
  ```
262
181
 
263
- ---
264
-
265
- ## 🎯 Events
266
-
267
- Listen to DOM events dispatched on the input element:
268
-
269
- | Event | Description |
270
- | -------------------------- | ---------------------------------- |
271
- | `timepicker:open` | Fired when timepicker opens |
272
- | `timepicker:cancel` | Fired when user cancels |
273
- | `timepicker:confirm` | Fired when time is confirmed |
274
- | `timepicker:update` | Fired during clock interaction |
275
- | `timepicker:select-hour` | Fired when hour mode is selected |
276
- | `timepicker:select-minute` | Fired when minute mode is selected |
277
- | `timepicker:select-am` | Fired when AM is selected |
278
- | `timepicker:select-pm` | Fired when PM is selected |
279
- | `timepicker:error` | Fired when input validation fails |
280
-
281
- ### Event Usage
282
-
283
- ```javascript
284
- const input = document.querySelector("#timepicker");
285
- const picker = new TimepickerUI(input);
286
- picker.create();
287
-
288
- // Listen to events
289
- input.addEventListener("timepicker:confirm", (e) => {
290
- console.log("Time confirmed:", e.detail);
291
- // e.detail contains: { hour, minutes, type, degreesHours, degreesMinutes }
292
- });
293
-
294
- input.addEventListener("timepicker:cancel", (e) => {
295
- console.log("Cancelled:", e.detail);
296
- });
297
-
298
- input.addEventListener("timepicker:error", (e) => {
299
- console.error("Error:", e.detail.error);
300
- });
301
- ```
302
-
303
- ---
304
-
305
- ## 🛠️ API Methods
182
+ ## API Methods
306
183
 
307
184
  ### Instance Methods
308
185
 
309
186
  ```javascript
310
187
  const picker = new TimepickerUI(input, options);
311
188
 
312
- // Core methods
313
- picker.create(); // Initialize the timepicker
314
- picker.open(); // Open the timepicker programmatically
315
- picker.close(); // Close the timepicker
316
- picker.destroy(); // Destroy instance and clean up
317
-
318
- // Value methods
319
- picker.getValue(); // Get current time value
320
- picker.setValue("14:30"); // Set time programmatically
321
-
322
- // Configuration methods
323
- picker.update({ options: newOptions }); // Update configuration
324
- picker.getElement(); // Get the DOM element
189
+ picker.create(); // Initialize
190
+ picker.open(); // Open programmatically
191
+ picker.close(); // Close
192
+ picker.destroy(); // Clean up
193
+ picker.getValue(); // Get current time
194
+ picker.setValue("14:30"); // Set time
195
+ picker.update({ options }); // Update configuration
325
196
  ```
326
197
 
327
198
  ### Static Methods
328
199
 
329
200
  ```javascript
330
- // Instance management
331
201
  TimepickerUI.getById("my-id"); // Get instance by ID
332
- TimepickerUI.getAllInstances(); // Get all active instances
202
+ TimepickerUI.getAllInstances(); // Get all instances
333
203
  TimepickerUI.destroyAll(); // Destroy all instances
334
- TimepickerUI.isAvailable(element); // Check if element exists
335
204
  ```
336
205
 
337
- ### Method Examples
338
-
339
- ```javascript
340
- // Get current value
341
- const currentTime = picker.getValue();
342
- console.log(currentTime);
343
- // Output: { hour: '14', minutes: '30', type: '', time: '14:30', degreesHours: 30, degreesMinutes: 180 }
344
-
345
- // Set new time
346
- picker.setValue("09:15 AM");
347
-
348
- // Update configuration
349
- picker.update({
350
- options: { theme: "dark", clockType: "24h" },
351
- create: true, // Reinitialize after update
352
- });
353
-
354
- // Instance management
355
- const picker1 = new TimepickerUI("#picker1", { id: "picker-1" });
356
- const picker2 = new TimepickerUI("#picker2", { id: "picker-2" });
357
-
358
- // Later...
359
- const foundPicker = TimepickerUI.getById("picker-1");
360
- ```
206
+ ## Events
361
207
 
362
- ---
208
+ Listen to timepicker events using the new **EventEmitter API** (recommended) or the legacy DOM events (deprecated, will be removed in v4):
363
209
 
364
- ## 🆕 What's New in v3.0
210
+ ### EventEmitter API (Recommended)
365
211
 
366
- ### ✅ New Features
212
+ ```javascript
213
+ const picker = new TimepickerUI(input);
214
+ picker.create();
367
215
 
368
- - **Inline Mode**: Always-visible timepicker without modal overlay
369
- - **Instance Management**: `getById()`, `destroyAll()`, and custom instance IDs
370
- - **Callback System**: Direct callback functions instead of manual event listeners
371
- - **New Themes**: Added `dark`, `glassmorphic`, `pastel`, `ai`, `cyberpunk`
372
- - **Enhanced API**: `getValue()`, `setValue()`, improved `destroy()`
373
- - **SSR Compatibility**: Better support for server-side rendering
374
- - **TypeScript Improvements**: Complete type definitions and better IntelliSense
216
+ picker.on("confirm", (data) => {
217
+ console.log("Time confirmed:", data);
218
+ });
375
219
 
376
- ### 🔄 Breaking Changes
220
+ picker.on("cancel", (data) => {
221
+ console.log("Cancelled:", data);
222
+ });
377
223
 
378
- - **Event Names**: All events now use `timepicker:` prefix
379
- - **Destroy Behavior**: `.destroy()` no longer removes input from DOM
380
- - **Theme Options**: Some theme names have changed
381
- - **API Changes**: Some method signatures have been updated
382
- - **Styles are no longer auto-loaded**
383
- You must now explicitly import CSS files. Use:
224
+ picker.on("open", () => {
225
+ console.log("Picker opened");
226
+ });
384
227
 
385
- - `main.css` core styles with `basic` theme only
386
- - `index.css` all styles including all themes
387
- - or import specific themes from `themes/`
228
+ picker.on("update", (data) => {
229
+ console.log("Time updated:", data);
230
+ });
388
231
 
389
- ---
232
+ picker.on("select:hour", (data) => {
233
+ console.log("Hour selected:", data.hour);
234
+ });
390
235
 
391
- ## 📈 Upgrade Guide: v2 v3
236
+ picker.on("select:minute", (data) => {
237
+ console.log("Minute selected:", data.minutes);
238
+ });
392
239
 
393
- ### 1. Update Event Listeners
240
+ picker.on("select:am", (data) => {
241
+ console.log("AM selected");
242
+ });
394
243
 
395
- **v2 (Old):**
244
+ picker.on("select:pm", (data) => {
245
+ console.log("PM selected");
246
+ });
396
247
 
397
- ```javascript
398
- input.addEventListener('show', (e) => { ... });
399
- input.addEventListener('cancel', (e) => { ... });
400
- input.addEventListener('accept', (e) => { ... });
401
- ```
248
+ picker.on("error", (data) => {
249
+ console.log("Error:", data.error);
250
+ });
402
251
 
403
- **v3 (New):**
252
+ picker.once("confirm", (data) => {
253
+ console.log("This runs only once");
254
+ });
404
255
 
405
- ```javascript
406
- input.addEventListener('timepicker:open', (e) => { ... });
407
- input.addEventListener('timepicker:cancel', (e) => { ... });
408
- input.addEventListener('timepicker:confirm', (e) => { ... });
256
+ picker.off("confirm", handler);
409
257
  ```
410
258
 
411
- ### 2. Replace Event Listeners with Callbacks
259
+ ### Legacy DOM Events (Deprecated)
412
260
 
413
- **v2 (Old):**
261
+ **Note:** DOM events (e.g., `timepicker:confirm`) are deprecated and will be removed in v4. Please migrate to the new EventEmitter API shown above.
414
262
 
415
263
  ```javascript
416
- const picker = new TimepickerUI(input);
417
- input.addEventListener("accept", (e) => {
418
- console.log("Time selected:", e.detail);
264
+ input.addEventListener("timepicker:confirm", (e) => {
265
+ console.log("Time:", e.detail);
419
266
  });
420
- ```
421
-
422
- **v3 (New):**
423
267
 
424
- ```javascript
425
- const picker = new TimepickerUI(input, {
426
- onConfirm: (data) => {
427
- console.log("Time selected:", data);
428
- },
268
+ input.addEventListener("timepicker:cancel", (e) => {
269
+ console.log("Cancelled");
429
270
  });
430
271
  ```
431
272
 
432
- ### 3. Update Destroy Method Usage
273
+ 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`
433
274
 
434
- **v2 (Old):**
275
+ ## Upgrade Guide: v2 → v3
435
276
 
436
- ```javascript
437
- picker.destroy(); // This removed the input from DOM
438
- ```
277
+ ### Breaking Changes
439
278
 
440
- **v3 (New):**
279
+ **1. CSS must be imported manually**
441
280
 
442
281
  ```javascript
443
- picker.destroy(); // Only destroys timepicker, keeps input intact
444
- // If you need to remove input, do it manually:
445
- // input.remove();
282
+ // v3 - Required
283
+ import "timepicker-ui/main.css";
446
284
  ```
447
285
 
448
- ### 4. Theme Updates
449
-
450
- **v2 (Old):**
286
+ **2. Event names changed**
451
287
 
452
288
  ```javascript
453
- // Limited theme options
454
- theme: "basic" | "crane-straight" | "crane-radius" | "m3";
455
- ```
289
+ // v2
290
+ input.addEventListener("show", ...);
291
+ input.addEventListener("accept", ...);
456
292
 
457
- **v3 (New):**
458
-
459
- ```javascript
460
- // Extended theme options
461
- theme: "basic" |
462
- "crane-straight" |
463
- "crane-radius" |
464
- "m3" |
465
- "dark" |
466
- "glassmorphic" |
467
- "pastel" |
468
- "ai" |
469
- "cyberpunk";
293
+ // v3
294
+ input.addEventListener("timepicker:open", ...);
295
+ input.addEventListener("timepicker:confirm", ...);
470
296
  ```
471
297
 
472
- ### 5. Add Inline Mode (Optional)
473
-
474
- **v3 New Feature:**
298
+ **3. Use callbacks instead of events**
475
299
 
476
300
  ```javascript
301
+ // v3 - Recommended
477
302
  const picker = new TimepickerUI(input, {
478
- inline: {
479
- enabled: true,
480
- containerId: "timepicker-container",
481
- showButtons: false,
482
- autoUpdate: true,
483
- },
303
+ onConfirm: (data) => console.log(data),
304
+ onCancel: (data) => console.log("Cancelled"),
484
305
  });
485
306
  ```
486
307
 
487
- ### 6. Instance Management (Optional)
488
-
489
- **v3 New Feature:**
308
+ **4. destroy() behavior**
490
309
 
491
310
  ```javascript
492
- // Create with custom ID
493
- const picker = new TimepickerUI(input, { id: "my-timepicker" });
494
-
495
- // Later, get by ID
496
- const foundPicker = TimepickerUI.getById("my-timepicker");
311
+ // v2 - Removed input from DOM
312
+ picker.destroy();
497
313
 
498
- // Destroy all instances
499
- TimepickerUI.destroyAll();
314
+ // v3 - Only destroys picker, keeps input
315
+ picker.destroy();
500
316
  ```
501
317
 
502
- ### 7. Import CSS Manually (New Requirement)
318
+ ### New in v3
503
319
 
504
- **v2 (Old):**
505
- Styles were bundled and automatically injected.
320
+ - Inline mode
321
+ - Instance management (`getById`, `destroyAll`)
322
+ - Direct callbacks
323
+ - 5 new themes
324
+ - `getValue()` and `setValue()` methods
325
+ - Better TypeScript support
326
+ - **EventEmitter API** for modern event handling (`on`, `off`, `once`)
506
327
 
507
- **v3 (New):**
508
- You must now import the styles yourself:
328
+ ### Migration to EventEmitter (v3.x)
509
329
 
510
- #### Option 1 All-in-one (includes every theme):
511
-
512
- ```js
513
- import "timepicker-ui/index.css";
514
- ```
330
+ Starting in v3.x, we recommend using the new EventEmitter API instead of DOM events:
515
331
 
516
- #### Option 2 – Core only (main styles + basic theme):
332
+ ```javascript
333
+ // Old way (deprecated, will be removed in v4)
334
+ input.addEventListener("timepicker:confirm", (e) => {
335
+ console.log(e.detail);
336
+ });
517
337
 
518
- ```js
519
- import "timepicker-ui/main.css";
338
+ // New way (recommended)
339
+ picker.on("confirm", (data) => {
340
+ console.log(data);
341
+ });
520
342
  ```
521
343
 
522
- #### Option 3 – Use only the theme you need:
344
+ Benefits:
523
345
 
524
- ```js
525
- import "timepicker-ui/main.css"; // Required base
526
- import "timepicker-ui/theme-dark.css"; // Or any other theme
527
- ```
528
-
529
- ---
346
+ - Cleaner API without prefixes
347
+ - Better TypeScript support
348
+ - No DOM pollution
349
+ - Memory-efficient (automatic cleanup on destroy)
350
+ - Supports `once()` for one-time listeners
530
351
 
531
- ## 🌐 Framework Integration
352
+ ## Framework Integration
532
353
 
533
354
  ### React
534
355
 
535
- ```tsx
356
+ ```jsx
536
357
  import { useEffect, useRef } from "react";
537
358
  import { TimepickerUI } from "timepicker-ui";
538
359
  import "timepicker-ui/main.css";
539
- import "timepicker-ui/theme-cyberpunk.css";
540
360
 
541
361
  function App() {
542
- const inputRef = useRef<HTMLInputElement>(null);
362
+ const inputRef = useRef(null);
543
363
 
544
364
  useEffect(() => {
545
365
  if (!inputRef.current) return;
546
- const picker = new TimepickerUI(inputRef.current, {
547
- theme: "cyberpunk",
548
- });
366
+ const picker = new TimepickerUI(inputRef.current);
549
367
  picker.create();
550
- return () => {
551
- picker.destroy();
552
- };
368
+ return () => picker.destroy();
553
369
  }, []);
554
370
 
555
- return (
556
- <div style={{ padding: "2rem" }}>
557
- <h1>Timepicker UI React Demo</h1>
558
- <input ref={inputRef} placeholder="Click me..." />
559
- </div>
560
- );
371
+ return <input ref={inputRef} placeholder="Select time" />;
561
372
  }
562
-
563
- export default App;
564
373
  ```
565
374
 
566
- > ℹ️ **Note:** Don't forget to include global `box-sizing` rule in your `styles.css` (see [Global CSS Required](#global-css-required)).
567
-
568
375
  ### Vue 3
569
376
 
570
377
  ```vue
571
378
  <template>
572
- <div style="padding: 2rem">
573
- <h1>Timepicker UI – Vue Demo</h1>
574
- <input ref="pickerInput" placeholder="Pick a time..." />
575
- </div>
379
+ <input ref="pickerInput" placeholder="Select time" />
576
380
  </template>
577
381
 
578
382
  <script setup>
579
383
  import { onMounted, ref } from "vue";
580
384
  import { TimepickerUI } from "timepicker-ui";
581
385
  import "timepicker-ui/main.css";
582
- import "timepicker-ui/theme-glassmorphic.css";
583
386
 
584
387
  const pickerInput = ref(null);
585
388
 
586
389
  onMounted(() => {
587
390
  if (!pickerInput.value) return;
588
-
589
- const picker = new TimepickerUI(pickerInput.value, {
590
- theme: "glassmorphic",
591
- });
391
+ const picker = new TimepickerUI(pickerInput.value);
592
392
  picker.create();
593
393
  });
594
394
  </script>
595
395
  ```
596
396
 
597
- > ℹ️ **Note:** Don't forget to include global `box-sizing` rule in your `styles.css` (see [Global CSS Required](#global-css-required)).
598
-
599
- ## Angular
600
-
601
- ### `app.ts`
397
+ ### Angular
602
398
 
603
- ```ts
604
- import {
605
- Component,
606
- ElementRef,
607
- AfterViewInit,
608
- ViewChild,
609
- signal,
610
- } from "@angular/core";
399
+ ```typescript
400
+ import { Component, ElementRef, ViewChild, AfterViewInit } from "@angular/core";
611
401
  import { TimepickerUI } from "timepicker-ui";
612
402
 
613
403
  @Component({
614
404
  selector: "app-root",
615
- standalone: true,
616
- templateUrl: "./app.html",
617
- styleUrl: "./app.css",
405
+ template: `<input #timepickerInput placeholder="Select time" />`,
618
406
  })
619
407
  export class App implements AfterViewInit {
620
- protected readonly title = signal("timepicker-ui-demo");
621
-
622
- @ViewChild("timepickerInput", { static: true })
623
- inputRef!: ElementRef<HTMLInputElement>;
408
+ @ViewChild("timepickerInput") inputRef!: ElementRef<HTMLInputElement>;
624
409
 
625
- ngAfterViewInit(): void {
626
- const picker = new TimepickerUI(this.inputRef.nativeElement, {
627
- theme: "glassmorphic",
628
- });
410
+ ngAfterViewInit() {
411
+ const picker = new TimepickerUI(this.inputRef.nativeElement);
629
412
  picker.create();
630
413
  }
631
414
  }
632
415
  ```
633
416
 
634
- ### `app.html`
635
-
636
- ```html
637
- <input #timepickerInput placeholder="Select time..." />
638
- ```
639
-
640
- ### `angular.json` – styles section
417
+ Add to `angular.json` styles:
641
418
 
642
419
  ```json
643
- "styles": [
644
- "src/styles.css",
645
- "timepicker-ui/main.css"
646
- ]
420
+ "styles": ["src/styles.css", "timepicker-ui/main.css"]
647
421
  ```
648
422
 
649
- > ℹ️ **Note:** Don't forget to include global `box-sizing` rule in your `styles.css` (see [Global CSS Required](#global-css-required)).
650
-
651
- ---
652
-
653
- ## 🔧 Development
423
+ ## Development
654
424
 
655
- All development and build tooling is located in the [`app/`](./app) directory.
656
- Please refer to [`app/README.md`](./app/README.md) for instructions on running the development server, building the library, running tests, and using the full toolchain.
425
+ Development tooling is in the [`app/`](./app) directory. See [`app/README.md`](./app/README.md) for details.
657
426
 
658
- ---
659
-
660
- ## 📄 License
427
+ ## License
661
428
 
662
429
  MIT © [Piotr Glejzer](https://github.com/pglejzer)
663
430
 
664
- ---
665
-
666
- ## 🤝 Contributing
667
-
668
- Contributions, issues, and feature requests are welcome! Feel free to check the [issues page](https://github.com/pglejzer/timepicker-ui/issues).
669
-
670
- ---
431
+ ## Contributing
671
432
 
672
- ## 📊 Browser Support
433
+ Contributions welcome! Check the [issues page](https://github.com/pglejzer/timepicker-ui/issues).
673
434
 
674
- - Chrome 60+
675
- - Firefox 55+
676
- - Safari 12+
677
- - Edge 79+
678
- - iOS Safari 12+
679
- - Chrome Android 60+
435
+ ## Browser Support
680
436
 
681
- ---
437
+ Chrome 60+, Firefox 55+, Safari 12+, Edge 79+, iOS Safari 12+, Chrome Android 60+
682
438
 
683
- ## 🙋‍♂️ Support
439
+ ## Support
684
440
 
685
- - 📖 [Documentation](https://pglejzer.github.io/timepicker-ui-docs/)
686
- - 🐛 [Report Bug](https://github.com/pglejzer/timepicker-ui/issues)
687
- - 💡 [Request Feature](https://github.com/pglejzer/timepicker-ui/issues)
688
- - 💬 [Discussions](https://github.com/pglejzer/timepicker-ui/discussions)
441
+ - [Documentation](https://pglejzer.github.io/timepicker-ui-docs/)
442
+ - [Report Bug](https://github.com/pglejzer/timepicker-ui/issues)
443
+ - [Discussions](https://github.com/pglejzer/timepicker-ui/discussions)