timepicker-ui 3.0.0 → 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 +223 -432
- 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 +4 -3
package/README.md
CHANGED
|
@@ -1,60 +1,58 @@
|
|
|
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
|
-
|
|
9
|
+
[Live Demo](https://timepicker-ui.vercel.app/) • [Documentation](https://timepicker-ui.vercel.app/docs) • [Changelog](./CHANGELOG.md)
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
**Upgrading from v2?** Check the [upgrade guide](#upgrade-guide-v2--v3) below.
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
👉 [Click here to see live examples](https://timepicker-ui.vercel.app/)
|
|
13
|
+
## Features
|
|
19
14
|
|
|
20
|
-
|
|
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
|
|
21
23
|
|
|
22
|
-
##
|
|
24
|
+
## Known Limitations
|
|
23
25
|
|
|
24
|
-
|
|
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
|
|
26
|
+
This project is actively maintained. Some areas planned for improvement:
|
|
33
27
|
|
|
34
|
-
|
|
28
|
+
- No unit/integration tests yet
|
|
29
|
+
- Some files need refactoring
|
|
30
|
+
- A few `any` types remain in the codebase
|
|
31
|
+
- No performance monitoring
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
Contributions welcome! Feel free to [open an issue or PR](https://github.com/pglejzer/timepicker-ui/issues).
|
|
37
34
|
|
|
38
|
-
|
|
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**
|
|
35
|
+
## Installation
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
```bash
|
|
38
|
+
npm install timepicker-ui
|
|
39
|
+
```
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
## Important: Global CSS Required
|
|
46
42
|
|
|
47
|
-
|
|
43
|
+
Your app needs this global CSS rule for correct styling:
|
|
48
44
|
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
```css
|
|
46
|
+
*,
|
|
47
|
+
*::before,
|
|
48
|
+
*::after {
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
}
|
|
53
51
|
```
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
Most projects include this by default.
|
|
56
54
|
|
|
57
|
-
##
|
|
55
|
+
## Quick Start
|
|
58
56
|
|
|
59
57
|
### Basic Usage
|
|
60
58
|
|
|
@@ -64,6 +62,7 @@ yarn add timepicker-ui
|
|
|
64
62
|
|
|
65
63
|
```javascript
|
|
66
64
|
import { TimepickerUI } from "timepicker-ui";
|
|
65
|
+
import "timepicker-ui/main.css";
|
|
67
66
|
|
|
68
67
|
const input = document.querySelector("#timepicker");
|
|
69
68
|
const picker = new TimepickerUI(input);
|
|
@@ -78,15 +77,19 @@ const picker = new TimepickerUI(input, {
|
|
|
78
77
|
clockType: "24h",
|
|
79
78
|
animation: true,
|
|
80
79
|
backdrop: true,
|
|
80
|
+
onConfirm: (data) => {
|
|
81
|
+
console.log("Selected time:", data);
|
|
82
|
+
},
|
|
81
83
|
});
|
|
82
84
|
picker.create();
|
|
83
85
|
```
|
|
84
86
|
|
|
85
|
-
### React
|
|
87
|
+
### React Example
|
|
86
88
|
|
|
87
89
|
```tsx
|
|
88
90
|
import { useEffect, useRef } from "react";
|
|
89
91
|
import { TimepickerUI } from "timepicker-ui";
|
|
92
|
+
import "timepicker-ui/main.css";
|
|
90
93
|
|
|
91
94
|
function TimePickerComponent() {
|
|
92
95
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
@@ -108,439 +111,264 @@ function TimePickerComponent() {
|
|
|
108
111
|
}
|
|
109
112
|
```
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
| `switchToMinutesAfterSelectHour` | `boolean` | `true` | Auto-switch to minutes after hour selection |
|
|
145
|
-
| `theme` | Theme | `"basic"` | UI theme (see themes section) |
|
|
146
|
-
| `timeLabel` | `string` | `"Select Time"` | Time label for desktop version |
|
|
147
|
-
|
|
148
|
-
### 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`
|
|
149
147
|
|
|
150
148
|
```javascript
|
|
149
|
+
import "timepicker-ui/main.css"; // Required base styles
|
|
150
|
+
import "timepicker-ui/theme-dark.css"; // Specific theme
|
|
151
|
+
|
|
151
152
|
const picker = new TimepickerUI(input, {
|
|
152
|
-
|
|
153
|
-
enabled: true,
|
|
154
|
-
containerId: "timepicker-container",
|
|
155
|
-
showButtons: false, // Hide OK/Cancel buttons
|
|
156
|
-
autoUpdate: true, // Auto-update input on change
|
|
157
|
-
},
|
|
153
|
+
theme: "dark",
|
|
158
154
|
});
|
|
159
155
|
```
|
|
160
156
|
|
|
161
|
-
### Disabled Time
|
|
157
|
+
### Disabled Time
|
|
162
158
|
|
|
163
159
|
```javascript
|
|
164
160
|
const picker = new TimepickerUI(input, {
|
|
165
161
|
disabledTime: {
|
|
166
|
-
hours: [1, 3, 5, 8],
|
|
167
|
-
minutes: [15, 30, 45],
|
|
168
|
-
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",
|
|
169
165
|
},
|
|
170
166
|
});
|
|
171
167
|
```
|
|
172
168
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
## 🎨 Themes
|
|
176
|
-
|
|
177
|
-
> **Note:** As of v3.0, you must import CSS styles manually. See [Upgrade Guide](#upgrade-guide-v2--v3) for details.
|
|
178
|
-
|
|
179
|
-
Choose from 9 built-in themes:
|
|
180
|
-
|
|
181
|
-
| Theme | Description |
|
|
182
|
-
| ---------------- | -------------------------------------- |
|
|
183
|
-
| `basic` | Default Material Design theme |
|
|
184
|
-
| `crane-straight` | Google Crane theme with straight edges |
|
|
185
|
-
| `crane-radius` | Google Crane theme with rounded edges |
|
|
186
|
-
| `m3` | Material Design 3 (Material You) |
|
|
187
|
-
| `dark` | Dark mode theme |
|
|
188
|
-
| `glassmorphic` | Modern glass effect |
|
|
189
|
-
| `pastel` | Soft pastel colors |
|
|
190
|
-
| `ai` | Futuristic AI-inspired theme |
|
|
191
|
-
| `cyberpunk` | Neon cyberpunk aesthetic |
|
|
192
|
-
|
|
193
|
-
```javascript
|
|
194
|
-
const picker = new TimepickerUI(input, {
|
|
195
|
-
theme: "cyberpunk",
|
|
196
|
-
});
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
## 📞 Callbacks
|
|
202
|
-
|
|
203
|
-
Configure callback functions to handle timepicker events:
|
|
204
|
-
|
|
205
|
-
| Callback | Type | Description |
|
|
206
|
-
| ---------------- | ---------------- | ---------------------------------------------- |
|
|
207
|
-
| `onOpen` | `(data) => void` | Triggered when timepicker opens |
|
|
208
|
-
| `onCancel` | `(data) => void` | Triggered when picker is cancelled |
|
|
209
|
-
| `onConfirm` | `(data) => void` | Triggered when time is confirmed (OK clicked) |
|
|
210
|
-
| `onUpdate` | `(data) => void` | Triggered during clock interaction (real-time) |
|
|
211
|
-
| `onSelectHour` | `(data) => void` | Triggered when hour mode is activated |
|
|
212
|
-
| `onSelectMinute` | `(data) => void` | Triggered when minute mode is activated |
|
|
213
|
-
| `onSelectAM` | `(data) => void` | Triggered when AM is selected |
|
|
214
|
-
| `onSelectPM` | `(data) => void` | Triggered when PM is selected |
|
|
215
|
-
| `onError` | `(data) => void` | Triggered when invalid time format is detected |
|
|
216
|
-
|
|
217
|
-
### Callback Data Structure
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
interface CallbackData {
|
|
221
|
-
hour?: string;
|
|
222
|
-
minutes?: string;
|
|
223
|
-
type?: string; // 'AM' or 'PM'
|
|
224
|
-
degreesHours?: number;
|
|
225
|
-
degreesMinutes?: number;
|
|
226
|
-
error?: string; // Only for onError
|
|
227
|
-
// ... additional context data
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Example Usage
|
|
169
|
+
### Inline Mode
|
|
232
170
|
|
|
233
171
|
```javascript
|
|
234
172
|
const picker = new TimepickerUI(input, {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
},
|
|
241
|
-
onError: (data) => {
|
|
242
|
-
alert(`Invalid time format: ${data.error}`);
|
|
173
|
+
inline: {
|
|
174
|
+
enabled: true,
|
|
175
|
+
containerId: "timepicker-container",
|
|
176
|
+
showButtons: false,
|
|
177
|
+
autoUpdate: true,
|
|
243
178
|
},
|
|
244
179
|
});
|
|
245
180
|
```
|
|
246
181
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
## 🎯 Events
|
|
250
|
-
|
|
251
|
-
Listen to DOM events dispatched on the input element:
|
|
252
|
-
|
|
253
|
-
| Event | Description |
|
|
254
|
-
| -------------------------- | ---------------------------------- |
|
|
255
|
-
| `timepicker:open` | Fired when timepicker opens |
|
|
256
|
-
| `timepicker:cancel` | Fired when user cancels |
|
|
257
|
-
| `timepicker:confirm` | Fired when time is confirmed |
|
|
258
|
-
| `timepicker:update` | Fired during clock interaction |
|
|
259
|
-
| `timepicker:select-hour` | Fired when hour mode is selected |
|
|
260
|
-
| `timepicker:select-minute` | Fired when minute mode is selected |
|
|
261
|
-
| `timepicker:select-am` | Fired when AM is selected |
|
|
262
|
-
| `timepicker:select-pm` | Fired when PM is selected |
|
|
263
|
-
| `timepicker:error` | Fired when input validation fails |
|
|
264
|
-
|
|
265
|
-
### Event Usage
|
|
266
|
-
|
|
267
|
-
```javascript
|
|
268
|
-
const input = document.querySelector("#timepicker");
|
|
269
|
-
const picker = new TimepickerUI(input);
|
|
270
|
-
picker.create();
|
|
271
|
-
|
|
272
|
-
// Listen to events
|
|
273
|
-
input.addEventListener("timepicker:confirm", (e) => {
|
|
274
|
-
console.log("Time confirmed:", e.detail);
|
|
275
|
-
// e.detail contains: { hour, minutes, type, degreesHours, degreesMinutes }
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
input.addEventListener("timepicker:cancel", (e) => {
|
|
279
|
-
console.log("Cancelled:", e.detail);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
input.addEventListener("timepicker:error", (e) => {
|
|
283
|
-
console.error("Error:", e.detail.error);
|
|
284
|
-
});
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
## 🛠️ API Methods
|
|
182
|
+
## API Methods
|
|
290
183
|
|
|
291
184
|
### Instance Methods
|
|
292
185
|
|
|
293
186
|
```javascript
|
|
294
187
|
const picker = new TimepickerUI(input, options);
|
|
295
188
|
|
|
296
|
-
//
|
|
297
|
-
picker.
|
|
298
|
-
picker.
|
|
299
|
-
picker.
|
|
300
|
-
picker.
|
|
301
|
-
|
|
302
|
-
//
|
|
303
|
-
picker.getValue(); // Get current time value
|
|
304
|
-
picker.setValue("14:30"); // Set time programmatically
|
|
305
|
-
|
|
306
|
-
// Configuration methods
|
|
307
|
-
picker.update({ options: newOptions }); // Update configuration
|
|
308
|
-
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
|
|
309
196
|
```
|
|
310
197
|
|
|
311
198
|
### Static Methods
|
|
312
199
|
|
|
313
200
|
```javascript
|
|
314
|
-
// Instance management
|
|
315
201
|
TimepickerUI.getById("my-id"); // Get instance by ID
|
|
316
|
-
TimepickerUI.getAllInstances(); // Get all
|
|
202
|
+
TimepickerUI.getAllInstances(); // Get all instances
|
|
317
203
|
TimepickerUI.destroyAll(); // Destroy all instances
|
|
318
|
-
TimepickerUI.isAvailable(element); // Check if element exists
|
|
319
204
|
```
|
|
320
205
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
```javascript
|
|
324
|
-
// Get current value
|
|
325
|
-
const currentTime = picker.getValue();
|
|
326
|
-
console.log(currentTime);
|
|
327
|
-
// Output: { hour: '14', minutes: '30', type: '', time: '14:30', degreesHours: 30, degreesMinutes: 180 }
|
|
328
|
-
|
|
329
|
-
// Set new time
|
|
330
|
-
picker.setValue("09:15 AM");
|
|
331
|
-
|
|
332
|
-
// Update configuration
|
|
333
|
-
picker.update({
|
|
334
|
-
options: { theme: "dark", clockType: "24h" },
|
|
335
|
-
create: true, // Reinitialize after update
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// Instance management
|
|
339
|
-
const picker1 = new TimepickerUI("#picker1", { id: "picker-1" });
|
|
340
|
-
const picker2 = new TimepickerUI("#picker2", { id: "picker-2" });
|
|
341
|
-
|
|
342
|
-
// Later...
|
|
343
|
-
const foundPicker = TimepickerUI.getById("picker-1");
|
|
344
|
-
```
|
|
206
|
+
## Events
|
|
345
207
|
|
|
346
|
-
|
|
208
|
+
Listen to timepicker events using the new **EventEmitter API** (recommended) or the legacy DOM events (deprecated, will be removed in v4):
|
|
347
209
|
|
|
348
|
-
|
|
210
|
+
### EventEmitter API (Recommended)
|
|
349
211
|
|
|
350
|
-
|
|
212
|
+
```javascript
|
|
213
|
+
const picker = new TimepickerUI(input);
|
|
214
|
+
picker.create();
|
|
351
215
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
- **New Themes**: Added `dark`, `glassmorphic`, `pastel`, `ai`, `cyberpunk`
|
|
356
|
-
- **Enhanced API**: `getValue()`, `setValue()`, improved `destroy()`
|
|
357
|
-
- **SSR Compatibility**: Better support for server-side rendering
|
|
358
|
-
- **TypeScript Improvements**: Complete type definitions and better IntelliSense
|
|
216
|
+
picker.on("confirm", (data) => {
|
|
217
|
+
console.log("Time confirmed:", data);
|
|
218
|
+
});
|
|
359
219
|
|
|
360
|
-
|
|
220
|
+
picker.on("cancel", (data) => {
|
|
221
|
+
console.log("Cancelled:", data);
|
|
222
|
+
});
|
|
361
223
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
- **API Changes**: Some method signatures have been updated
|
|
366
|
-
- **Styles are no longer auto-loaded**
|
|
367
|
-
You must now explicitly import CSS files. Use:
|
|
224
|
+
picker.on("open", () => {
|
|
225
|
+
console.log("Picker opened");
|
|
226
|
+
});
|
|
368
227
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
228
|
+
picker.on("update", (data) => {
|
|
229
|
+
console.log("Time updated:", data);
|
|
230
|
+
});
|
|
372
231
|
|
|
373
|
-
|
|
232
|
+
picker.on("select:hour", (data) => {
|
|
233
|
+
console.log("Hour selected:", data.hour);
|
|
234
|
+
});
|
|
374
235
|
|
|
375
|
-
|
|
236
|
+
picker.on("select:minute", (data) => {
|
|
237
|
+
console.log("Minute selected:", data.minutes);
|
|
238
|
+
});
|
|
376
239
|
|
|
377
|
-
|
|
240
|
+
picker.on("select:am", (data) => {
|
|
241
|
+
console.log("AM selected");
|
|
242
|
+
});
|
|
378
243
|
|
|
379
|
-
|
|
244
|
+
picker.on("select:pm", (data) => {
|
|
245
|
+
console.log("PM selected");
|
|
246
|
+
});
|
|
380
247
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
input.addEventListener('accept', (e) => { ... });
|
|
385
|
-
```
|
|
248
|
+
picker.on("error", (data) => {
|
|
249
|
+
console.log("Error:", data.error);
|
|
250
|
+
});
|
|
386
251
|
|
|
387
|
-
|
|
252
|
+
picker.once("confirm", (data) => {
|
|
253
|
+
console.log("This runs only once");
|
|
254
|
+
});
|
|
388
255
|
|
|
389
|
-
|
|
390
|
-
input.addEventListener('timepicker:open', (e) => { ... });
|
|
391
|
-
input.addEventListener('timepicker:cancel', (e) => { ... });
|
|
392
|
-
input.addEventListener('timepicker:confirm', (e) => { ... });
|
|
256
|
+
picker.off("confirm", handler);
|
|
393
257
|
```
|
|
394
258
|
|
|
395
|
-
###
|
|
259
|
+
### Legacy DOM Events (Deprecated)
|
|
396
260
|
|
|
397
|
-
**
|
|
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.
|
|
398
262
|
|
|
399
263
|
```javascript
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
console.log("Time selected:", e.detail);
|
|
264
|
+
input.addEventListener("timepicker:confirm", (e) => {
|
|
265
|
+
console.log("Time:", e.detail);
|
|
403
266
|
});
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
**v3 (New):**
|
|
407
267
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
onConfirm: (data) => {
|
|
411
|
-
console.log("Time selected:", data);
|
|
412
|
-
},
|
|
268
|
+
input.addEventListener("timepicker:cancel", (e) => {
|
|
269
|
+
console.log("Cancelled");
|
|
413
270
|
});
|
|
414
271
|
```
|
|
415
272
|
|
|
416
|
-
|
|
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`
|
|
417
274
|
|
|
418
|
-
|
|
275
|
+
## Upgrade Guide: v2 → v3
|
|
419
276
|
|
|
420
|
-
|
|
421
|
-
picker.destroy(); // This removed the input from DOM
|
|
422
|
-
```
|
|
277
|
+
### Breaking Changes
|
|
423
278
|
|
|
424
|
-
**
|
|
279
|
+
**1. CSS must be imported manually**
|
|
425
280
|
|
|
426
281
|
```javascript
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// input.remove();
|
|
282
|
+
// v3 - Required
|
|
283
|
+
import "timepicker-ui/main.css";
|
|
430
284
|
```
|
|
431
285
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
**v2 (Old):**
|
|
286
|
+
**2. Event names changed**
|
|
435
287
|
|
|
436
288
|
```javascript
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
**v3 (New):**
|
|
289
|
+
// v2
|
|
290
|
+
input.addEventListener("show", ...);
|
|
291
|
+
input.addEventListener("accept", ...);
|
|
442
292
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
"crane-straight" |
|
|
447
|
-
"crane-radius" |
|
|
448
|
-
"m3" |
|
|
449
|
-
"dark" |
|
|
450
|
-
"glassmorphic" |
|
|
451
|
-
"pastel" |
|
|
452
|
-
"ai" |
|
|
453
|
-
"cyberpunk";
|
|
293
|
+
// v3
|
|
294
|
+
input.addEventListener("timepicker:open", ...);
|
|
295
|
+
input.addEventListener("timepicker:confirm", ...);
|
|
454
296
|
```
|
|
455
297
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
**v3 New Feature:**
|
|
298
|
+
**3. Use callbacks instead of events**
|
|
459
299
|
|
|
460
300
|
```javascript
|
|
301
|
+
// v3 - Recommended
|
|
461
302
|
const picker = new TimepickerUI(input, {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
containerId: "timepicker-container",
|
|
465
|
-
showButtons: false,
|
|
466
|
-
autoUpdate: true,
|
|
467
|
-
},
|
|
303
|
+
onConfirm: (data) => console.log(data),
|
|
304
|
+
onCancel: (data) => console.log("Cancelled"),
|
|
468
305
|
});
|
|
469
306
|
```
|
|
470
307
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
**v3 New Feature:**
|
|
308
|
+
**4. destroy() behavior**
|
|
474
309
|
|
|
475
310
|
```javascript
|
|
476
|
-
//
|
|
477
|
-
|
|
311
|
+
// v2 - Removed input from DOM
|
|
312
|
+
picker.destroy();
|
|
478
313
|
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
// Destroy all instances
|
|
483
|
-
TimepickerUI.destroyAll();
|
|
314
|
+
// v3 - Only destroys picker, keeps input
|
|
315
|
+
picker.destroy();
|
|
484
316
|
```
|
|
485
317
|
|
|
486
|
-
###
|
|
487
|
-
|
|
488
|
-
**v2 (Old):**
|
|
489
|
-
Styles were bundled and automatically injected.
|
|
318
|
+
### New in v3
|
|
490
319
|
|
|
491
|
-
|
|
492
|
-
|
|
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`)
|
|
493
327
|
|
|
494
|
-
|
|
328
|
+
### Migration to EventEmitter (v3.x)
|
|
495
329
|
|
|
496
|
-
|
|
497
|
-
import "timepicker-ui/index.css";
|
|
498
|
-
```
|
|
330
|
+
Starting in v3.x, we recommend using the new EventEmitter API instead of DOM events:
|
|
499
331
|
|
|
500
|
-
|
|
332
|
+
```javascript
|
|
333
|
+
// Old way (deprecated, will be removed in v4)
|
|
334
|
+
input.addEventListener("timepicker:confirm", (e) => {
|
|
335
|
+
console.log(e.detail);
|
|
336
|
+
});
|
|
501
337
|
|
|
502
|
-
|
|
503
|
-
|
|
338
|
+
// New way (recommended)
|
|
339
|
+
picker.on("confirm", (data) => {
|
|
340
|
+
console.log(data);
|
|
341
|
+
});
|
|
504
342
|
```
|
|
505
343
|
|
|
506
|
-
|
|
344
|
+
Benefits:
|
|
507
345
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
|
512
351
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
## 🌐 Framework Integration
|
|
352
|
+
## Framework Integration
|
|
516
353
|
|
|
517
354
|
### React
|
|
518
355
|
|
|
519
|
-
```
|
|
356
|
+
```jsx
|
|
520
357
|
import { useEffect, useRef } from "react";
|
|
521
358
|
import { TimepickerUI } from "timepicker-ui";
|
|
359
|
+
import "timepicker-ui/main.css";
|
|
522
360
|
|
|
523
|
-
function
|
|
361
|
+
function App() {
|
|
524
362
|
const inputRef = useRef(null);
|
|
525
|
-
const pickerRef = useRef(null);
|
|
526
363
|
|
|
527
364
|
useEffect(() => {
|
|
528
|
-
if (inputRef.current)
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
},
|
|
534
|
-
});
|
|
535
|
-
pickerRef.current.create();
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
return () => {
|
|
539
|
-
pickerRef.current?.destroy();
|
|
540
|
-
};
|
|
541
|
-
}, [onChange]);
|
|
365
|
+
if (!inputRef.current) return;
|
|
366
|
+
const picker = new TimepickerUI(inputRef.current);
|
|
367
|
+
picker.create();
|
|
368
|
+
return () => picker.destroy();
|
|
369
|
+
}, []);
|
|
542
370
|
|
|
543
|
-
return <input ref={inputRef}
|
|
371
|
+
return <input ref={inputRef} placeholder="Select time" />;
|
|
544
372
|
}
|
|
545
373
|
```
|
|
546
374
|
|
|
@@ -548,105 +376,68 @@ function TimePicker({ onChange }) {
|
|
|
548
376
|
|
|
549
377
|
```vue
|
|
550
378
|
<template>
|
|
551
|
-
<input ref="
|
|
379
|
+
<input ref="pickerInput" placeholder="Select time" />
|
|
552
380
|
</template>
|
|
553
381
|
|
|
554
382
|
<script setup>
|
|
555
|
-
import {
|
|
383
|
+
import { onMounted, ref } from "vue";
|
|
556
384
|
import { TimepickerUI } from "timepicker-ui";
|
|
385
|
+
import "timepicker-ui/main.css";
|
|
557
386
|
|
|
558
|
-
const
|
|
559
|
-
let picker = null;
|
|
560
|
-
|
|
561
|
-
const emit = defineEmits(["time-selected"]);
|
|
387
|
+
const pickerInput = ref(null);
|
|
562
388
|
|
|
563
389
|
onMounted(() => {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
onConfirm: (data) => {
|
|
567
|
-
emit("time-selected", data);
|
|
568
|
-
},
|
|
569
|
-
});
|
|
390
|
+
if (!pickerInput.value) return;
|
|
391
|
+
const picker = new TimepickerUI(pickerInput.value);
|
|
570
392
|
picker.create();
|
|
571
393
|
});
|
|
572
|
-
|
|
573
|
-
onUnmounted(() => {
|
|
574
|
-
picker?.destroy();
|
|
575
|
-
});
|
|
576
394
|
</script>
|
|
577
395
|
```
|
|
578
396
|
|
|
579
397
|
### Angular
|
|
580
398
|
|
|
581
399
|
```typescript
|
|
582
|
-
import {
|
|
583
|
-
Component,
|
|
584
|
-
ElementRef,
|
|
585
|
-
ViewChild,
|
|
586
|
-
AfterViewInit,
|
|
587
|
-
OnDestroy,
|
|
588
|
-
} from "@angular/core";
|
|
400
|
+
import { Component, ElementRef, ViewChild, AfterViewInit } from "@angular/core";
|
|
589
401
|
import { TimepickerUI } from "timepicker-ui";
|
|
590
402
|
|
|
591
403
|
@Component({
|
|
592
|
-
selector: "app-
|
|
593
|
-
template:
|
|
404
|
+
selector: "app-root",
|
|
405
|
+
template: `<input #timepickerInput placeholder="Select time" />`,
|
|
594
406
|
})
|
|
595
|
-
export class
|
|
596
|
-
@ViewChild("timepickerInput") inputRef!: ElementRef
|
|
597
|
-
private picker!: TimepickerUI;
|
|
407
|
+
export class App implements AfterViewInit {
|
|
408
|
+
@ViewChild("timepickerInput") inputRef!: ElementRef<HTMLInputElement>;
|
|
598
409
|
|
|
599
410
|
ngAfterViewInit() {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
onConfirm: (data) => {
|
|
603
|
-
console.log("Time selected:", data);
|
|
604
|
-
},
|
|
605
|
-
});
|
|
606
|
-
this.picker.create();
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
ngOnDestroy() {
|
|
610
|
-
this.picker?.destroy();
|
|
411
|
+
const picker = new TimepickerUI(this.inputRef.nativeElement);
|
|
412
|
+
picker.create();
|
|
611
413
|
}
|
|
612
414
|
}
|
|
613
415
|
```
|
|
614
416
|
|
|
615
|
-
|
|
417
|
+
Add to `angular.json` styles:
|
|
616
418
|
|
|
617
|
-
|
|
419
|
+
```json
|
|
420
|
+
"styles": ["src/styles.css", "timepicker-ui/main.css"]
|
|
421
|
+
```
|
|
618
422
|
|
|
619
|
-
|
|
620
|
-
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.
|
|
423
|
+
## Development
|
|
621
424
|
|
|
622
|
-
|
|
425
|
+
Development tooling is in the [`app/`](./app) directory. See [`app/README.md`](./app/README.md) for details.
|
|
623
426
|
|
|
624
|
-
##
|
|
427
|
+
## License
|
|
625
428
|
|
|
626
429
|
MIT © [Piotr Glejzer](https://github.com/pglejzer)
|
|
627
430
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
## 🤝 Contributing
|
|
631
|
-
|
|
632
|
-
Contributions, issues, and feature requests are welcome! Feel free to check the [issues page](https://github.com/pglejzer/timepicker-ui/issues).
|
|
633
|
-
|
|
634
|
-
---
|
|
431
|
+
## Contributing
|
|
635
432
|
|
|
636
|
-
|
|
433
|
+
Contributions welcome! Check the [issues page](https://github.com/pglejzer/timepicker-ui/issues).
|
|
637
434
|
|
|
638
|
-
|
|
639
|
-
- Firefox 55+
|
|
640
|
-
- Safari 12+
|
|
641
|
-
- Edge 79+
|
|
642
|
-
- iOS Safari 12+
|
|
643
|
-
- Chrome Android 60+
|
|
435
|
+
## Browser Support
|
|
644
436
|
|
|
645
|
-
|
|
437
|
+
Chrome 60+, Firefox 55+, Safari 12+, Edge 79+, iOS Safari 12+, Chrome Android 60+
|
|
646
438
|
|
|
647
|
-
##
|
|
439
|
+
## Support
|
|
648
440
|
|
|
649
|
-
-
|
|
650
|
-
-
|
|
651
|
-
-
|
|
652
|
-
- 💬 [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)
|