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 +204 -449
- package/dist/css/index.css +1 -1
- package/dist/css/main.css +1 -1
- package/dist/css/themes/theme-ai.css +1 -1
- package/dist/css/themes/theme-crane.css +1 -1
- package/dist/css/themes/theme-custom.css +1 -0
- package/dist/css/themes/theme-cyberpunk.css +1 -1
- package/dist/css/themes/theme-dark.css +1 -1
- package/dist/css/themes/theme-glassmorphic.css +1 -1
- package/dist/css/themes/theme-m3.css +1 -1
- package/dist/css/themes/theme-pastel.css +1 -1
- package/dist/index.cjs +40 -38
- package/dist/index.d.cts +360 -299
- package/dist/index.d.ts +360 -299
- package/dist/index.js +40 -38
- package/dist/index.umd.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,62 +1,46 @@
|
|
|
1
1
|
# timepicker-ui
|
|
2
2
|
|
|
3
|
-
|
|
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
|
[](https://badge.fury.io/js/timepicker-ui)
|
|
10
6
|
[](https://npmcharts.com/compare/timepicker-ui?minimal=true)
|
|
11
7
|
[](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
|
-
##
|
|
13
|
+
## Features
|
|
23
14
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
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
|
-
##
|
|
24
|
+
## Known Limitations
|
|
35
25
|
|
|
36
|
-
This project is actively maintained
|
|
26
|
+
This project is actively maintained. Some areas planned for improvement:
|
|
37
27
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
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
|
|
157
|
+
### Disabled Time
|
|
178
158
|
|
|
179
159
|
```javascript
|
|
180
160
|
const picker = new TimepickerUI(input, {
|
|
181
161
|
disabledTime: {
|
|
182
|
-
hours: [1, 3, 5, 8],
|
|
183
|
-
minutes: [15, 30, 45],
|
|
184
|
-
interval: "10:00 AM - 2:00 PM",
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
//
|
|
313
|
-
picker.
|
|
314
|
-
picker.
|
|
315
|
-
picker.
|
|
316
|
-
picker.
|
|
317
|
-
|
|
318
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
210
|
+
### EventEmitter API (Recommended)
|
|
365
211
|
|
|
366
|
-
|
|
212
|
+
```javascript
|
|
213
|
+
const picker = new TimepickerUI(input);
|
|
214
|
+
picker.create();
|
|
367
215
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
220
|
+
picker.on("cancel", (data) => {
|
|
221
|
+
console.log("Cancelled:", data);
|
|
222
|
+
});
|
|
377
223
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
236
|
+
picker.on("select:minute", (data) => {
|
|
237
|
+
console.log("Minute selected:", data.minutes);
|
|
238
|
+
});
|
|
392
239
|
|
|
393
|
-
|
|
240
|
+
picker.on("select:am", (data) => {
|
|
241
|
+
console.log("AM selected");
|
|
242
|
+
});
|
|
394
243
|
|
|
395
|
-
|
|
244
|
+
picker.on("select:pm", (data) => {
|
|
245
|
+
console.log("PM selected");
|
|
246
|
+
});
|
|
396
247
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
input.addEventListener('accept', (e) => { ... });
|
|
401
|
-
```
|
|
248
|
+
picker.on("error", (data) => {
|
|
249
|
+
console.log("Error:", data.error);
|
|
250
|
+
});
|
|
402
251
|
|
|
403
|
-
|
|
252
|
+
picker.once("confirm", (data) => {
|
|
253
|
+
console.log("This runs only once");
|
|
254
|
+
});
|
|
404
255
|
|
|
405
|
-
|
|
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
|
-
###
|
|
259
|
+
### Legacy DOM Events (Deprecated)
|
|
412
260
|
|
|
413
|
-
**
|
|
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
|
-
|
|
417
|
-
|
|
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
|
-
|
|
425
|
-
|
|
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
|
-
|
|
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
|
-
|
|
275
|
+
## Upgrade Guide: v2 → v3
|
|
435
276
|
|
|
436
|
-
|
|
437
|
-
picker.destroy(); // This removed the input from DOM
|
|
438
|
-
```
|
|
277
|
+
### Breaking Changes
|
|
439
278
|
|
|
440
|
-
**
|
|
279
|
+
**1. CSS must be imported manually**
|
|
441
280
|
|
|
442
281
|
```javascript
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// input.remove();
|
|
282
|
+
// v3 - Required
|
|
283
|
+
import "timepicker-ui/main.css";
|
|
446
284
|
```
|
|
447
285
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
**v2 (Old):**
|
|
286
|
+
**2. Event names changed**
|
|
451
287
|
|
|
452
288
|
```javascript
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
289
|
+
// v2
|
|
290
|
+
input.addEventListener("show", ...);
|
|
291
|
+
input.addEventListener("accept", ...);
|
|
456
292
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
479
|
-
|
|
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
|
-
|
|
488
|
-
|
|
489
|
-
**v3 New Feature:**
|
|
308
|
+
**4. destroy() behavior**
|
|
490
309
|
|
|
491
310
|
```javascript
|
|
492
|
-
//
|
|
493
|
-
|
|
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
|
-
//
|
|
499
|
-
|
|
314
|
+
// v3 - Only destroys picker, keeps input
|
|
315
|
+
picker.destroy();
|
|
500
316
|
```
|
|
501
317
|
|
|
502
|
-
###
|
|
318
|
+
### New in v3
|
|
503
319
|
|
|
504
|
-
|
|
505
|
-
|
|
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
|
-
|
|
508
|
-
You must now import the styles yourself:
|
|
328
|
+
### Migration to EventEmitter (v3.x)
|
|
509
329
|
|
|
510
|
-
|
|
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
|
-
|
|
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
|
-
|
|
519
|
-
|
|
338
|
+
// New way (recommended)
|
|
339
|
+
picker.on("confirm", (data) => {
|
|
340
|
+
console.log(data);
|
|
341
|
+
});
|
|
520
342
|
```
|
|
521
343
|
|
|
522
|
-
|
|
344
|
+
Benefits:
|
|
523
345
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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
|
-
##
|
|
352
|
+
## Framework Integration
|
|
532
353
|
|
|
533
354
|
### React
|
|
534
355
|
|
|
535
|
-
```
|
|
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
|
|
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
|
-
<
|
|
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
|
-
|
|
598
|
-
|
|
599
|
-
## Angular
|
|
600
|
-
|
|
601
|
-
### `app.ts`
|
|
397
|
+
### Angular
|
|
602
398
|
|
|
603
|
-
```
|
|
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
|
-
|
|
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
|
-
|
|
621
|
-
|
|
622
|
-
@ViewChild("timepickerInput", { static: true })
|
|
623
|
-
inputRef!: ElementRef<HTMLInputElement>;
|
|
408
|
+
@ViewChild("timepickerInput") inputRef!: ElementRef<HTMLInputElement>;
|
|
624
409
|
|
|
625
|
-
ngAfterViewInit()
|
|
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
|
-
|
|
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
|
-
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
## 🔧 Development
|
|
423
|
+
## Development
|
|
654
424
|
|
|
655
|
-
|
|
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
|
-
|
|
433
|
+
Contributions welcome! Check the [issues page](https://github.com/pglejzer/timepicker-ui/issues).
|
|
673
434
|
|
|
674
|
-
|
|
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
|
-
##
|
|
439
|
+
## Support
|
|
684
440
|
|
|
685
|
-
-
|
|
686
|
-
-
|
|
687
|
-
-
|
|
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)
|