@nuralyui/timepicker 0.1.1
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/controllers/formatting.controller.d.ts +93 -0
- package/controllers/formatting.controller.d.ts.map +1 -0
- package/controllers/formatting.controller.js +195 -0
- package/controllers/formatting.controller.js.map +1 -0
- package/controllers/index.d.ts +9 -0
- package/controllers/index.d.ts.map +1 -0
- package/controllers/index.js +9 -0
- package/controllers/index.js.map +1 -0
- package/controllers/selection.controller.d.ts +72 -0
- package/controllers/selection.controller.d.ts.map +1 -0
- package/controllers/selection.controller.js +175 -0
- package/controllers/selection.controller.js.map +1 -0
- package/controllers/validation.controller.d.ts +88 -0
- package/controllers/validation.controller.d.ts.map +1 -0
- package/controllers/validation.controller.js +200 -0
- package/controllers/validation.controller.js.map +1 -0
- package/index.d.ts +12 -0
- package/index.js +12 -0
- package/interfaces/timepicker.interface.d.ts +103 -0
- package/interfaces/timepicker.interface.d.ts.map +1 -0
- package/interfaces/timepicker.interface.js +7 -0
- package/interfaces/timepicker.interface.js.map +1 -0
- package/package.json +63 -0
- package/test/timepicker.test.d.ts +7 -0
- package/test/timepicker.test.d.ts.map +1 -0
- package/test/timepicker.test.js +218 -0
- package/test/timepicker.test.js.map +1 -0
- package/timepicker.component.backup.d.ts +165 -0
- package/timepicker.component.backup.js +890 -0
- package/timepicker.component.clean.d.ts +95 -0
- package/timepicker.component.clean.js +471 -0
- package/timepicker.component.d.ts +117 -0
- package/timepicker.component.js +747 -0
- package/timepicker.constants.d.ts +85 -0
- package/timepicker.constants.js +85 -0
- package/timepicker.style.d.ts +7 -0
- package/timepicker.style.js +646 -0
- package/timepicker.types.d.ts +161 -0
- package/timepicker.types.js +125 -0
- package/utils/time.utils.d.ts +87 -0
- package/utils/time.utils.d.ts.map +1 -0
- package/utils/time.utils.js +235 -0
- package/utils/time.utils.js.map +1 -0
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, html, nothing } from 'lit';
|
|
8
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
9
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
10
|
+
// Import UI components
|
|
11
|
+
import '../input/input.component.js';
|
|
12
|
+
import '../button/button.component.js';
|
|
13
|
+
// Import base mixin and types
|
|
14
|
+
import { NuralyUIBaseMixin } from '../../shared/base-mixin.js';
|
|
15
|
+
import { SharedDropdownController } from '../../shared/controllers/dropdown.controller.js';
|
|
16
|
+
import { TimeFormat, TimePickerState, TimePickerPlacement, TimePeriod, TimeStep, EMPTY_TIME_VALUE, TIME_PICKER_EVENTS, } from './timepicker.types.js';
|
|
17
|
+
// Import controllers
|
|
18
|
+
import { TimePickerSelectionController } from './controllers/selection.controller.js';
|
|
19
|
+
import { TimePickerValidationController } from './controllers/validation.controller.js';
|
|
20
|
+
import { TimePickerFormattingController } from './controllers/formatting.controller.js';
|
|
21
|
+
// Import utilities
|
|
22
|
+
import { TimeUtils } from './utils/time.utils.js';
|
|
23
|
+
// Import styles
|
|
24
|
+
import { styles as timePickerStyles } from './timepicker.style.js';
|
|
25
|
+
/**
|
|
26
|
+
* A comprehensive time picker component that supports both 12-hour and 24-hour formats,
|
|
27
|
+
* with optional seconds display and extensive customization options.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```html
|
|
31
|
+
* <nr-timepicker
|
|
32
|
+
* value="14:30:00"
|
|
33
|
+
* format="24h"
|
|
34
|
+
* show-seconds
|
|
35
|
+
* placeholder="Select time">
|
|
36
|
+
* </nr-timepicker>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
let NrTimePickerElement = class NrTimePickerElement extends NuralyUIBaseMixin(LitElement) {
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
// Properties
|
|
43
|
+
this.value = '';
|
|
44
|
+
this.name = '';
|
|
45
|
+
this.placeholder = 'Select time';
|
|
46
|
+
this.format = TimeFormat.TwentyFourHour;
|
|
47
|
+
this.showSeconds = false;
|
|
48
|
+
this.disabled = false;
|
|
49
|
+
this.readonly = false;
|
|
50
|
+
this.required = false;
|
|
51
|
+
this.helperText = '';
|
|
52
|
+
this.label = '';
|
|
53
|
+
this.size = 'medium';
|
|
54
|
+
this.variant = 'outlined';
|
|
55
|
+
this.placement = TimePickerPlacement.Bottom;
|
|
56
|
+
/** Scroll behavior for dropdown navigation - 'instant' for immediate, 'smooth' for animated, 'auto' for browser default */
|
|
57
|
+
this.scrollBehavior = 'instant';
|
|
58
|
+
// State
|
|
59
|
+
this.inputValue = '';
|
|
60
|
+
this.state = TimePickerState.Default;
|
|
61
|
+
this.validationMessage = '';
|
|
62
|
+
// Controllers
|
|
63
|
+
this.dropdownController = new SharedDropdownController(this);
|
|
64
|
+
this.selectionController = new TimePickerSelectionController(this);
|
|
65
|
+
this.validationController = new TimePickerValidationController(this);
|
|
66
|
+
this.formattingController = new TimePickerFormattingController(this);
|
|
67
|
+
}
|
|
68
|
+
connectedCallback() {
|
|
69
|
+
super.connectedCallback();
|
|
70
|
+
this.updateConstraints();
|
|
71
|
+
if (this.value) {
|
|
72
|
+
this.setTimeFromValue(this.value);
|
|
73
|
+
}
|
|
74
|
+
// Add global click listener to close dropdown when clicking outside
|
|
75
|
+
this.addEventListener('click', this.handleComponentClick.bind(this));
|
|
76
|
+
document.addEventListener('click', this.handleDocumentClick.bind(this));
|
|
77
|
+
}
|
|
78
|
+
disconnectedCallback() {
|
|
79
|
+
super.disconnectedCallback();
|
|
80
|
+
// Clean up global event listeners
|
|
81
|
+
document.removeEventListener('click', this.handleDocumentClick.bind(this));
|
|
82
|
+
}
|
|
83
|
+
updated(changedProperties) {
|
|
84
|
+
super.updated(changedProperties);
|
|
85
|
+
if (changedProperties.has('value') && this.value !== this.inputValue) {
|
|
86
|
+
this.setTimeFromValue(this.value);
|
|
87
|
+
// Scroll to the selected time when value changes from outside
|
|
88
|
+
if (this.dropdownController.isOpen) {
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
this.scrollToSelectedTime();
|
|
91
|
+
}, 50);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (this.shouldUpdateConstraints(changedProperties)) {
|
|
95
|
+
this.updateConstraints();
|
|
96
|
+
}
|
|
97
|
+
// Set up dropdown elements
|
|
98
|
+
this.setupDropdownElements();
|
|
99
|
+
}
|
|
100
|
+
setupDropdownElements() {
|
|
101
|
+
var _a, _b;
|
|
102
|
+
const dropdown = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.time-picker__dropdown');
|
|
103
|
+
const trigger = (_b = this.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.time-picker__input-wrapper');
|
|
104
|
+
if (dropdown && trigger) {
|
|
105
|
+
this.dropdownController.setElements(dropdown, trigger);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
render() {
|
|
109
|
+
const wrapperClasses = {
|
|
110
|
+
'time-picker': true,
|
|
111
|
+
'time-picker--open': this.dropdownController.isOpen,
|
|
112
|
+
'time-picker--disabled': this.disabled,
|
|
113
|
+
'time-picker--readonly': this.readonly,
|
|
114
|
+
'time-picker--error': this.state === TimePickerState.Error,
|
|
115
|
+
};
|
|
116
|
+
return html `
|
|
117
|
+
<div class="${classMap(wrapperClasses)}" data-theme="${this.currentTheme}" part="wrapper">
|
|
118
|
+
${this.renderLabel()}
|
|
119
|
+
${this.renderInput()}
|
|
120
|
+
${this.renderDropdown()}
|
|
121
|
+
${this.renderHelperText()}
|
|
122
|
+
</div>
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
// Public API methods
|
|
126
|
+
open() {
|
|
127
|
+
this.dropdownController.open();
|
|
128
|
+
// Scroll to selected time when opening programmatically
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
this.scrollToSelectedTime();
|
|
131
|
+
}, 50);
|
|
132
|
+
}
|
|
133
|
+
close() { this.dropdownController.close(); }
|
|
134
|
+
clear() {
|
|
135
|
+
this.value = '';
|
|
136
|
+
this.inputValue = '';
|
|
137
|
+
this.selectionController.clearSelection();
|
|
138
|
+
}
|
|
139
|
+
setToNow() {
|
|
140
|
+
const now = TimeUtils.getCurrentTime();
|
|
141
|
+
this.selectionController.selectTime(now);
|
|
142
|
+
this.updateInputValue();
|
|
143
|
+
// Scroll to the newly selected time if dropdown is open
|
|
144
|
+
if (this.dropdownController.isOpen) {
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
this.scrollToSelectedTime();
|
|
147
|
+
}, 10);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
validate() {
|
|
151
|
+
const selectedTime = this.selectionController.getSelectedTime();
|
|
152
|
+
if (!selectedTime)
|
|
153
|
+
return true;
|
|
154
|
+
return this.validationController.validateConstraints(selectedTime);
|
|
155
|
+
}
|
|
156
|
+
validateTime(time) {
|
|
157
|
+
return this.validationController.validateConstraints(time);
|
|
158
|
+
}
|
|
159
|
+
// Helper methods for checking if individual time components are valid
|
|
160
|
+
isHourValid(hour, selectedTime) {
|
|
161
|
+
const testTime = Object.assign(Object.assign({}, selectedTime), { hours: hour });
|
|
162
|
+
return this.validateTime(testTime);
|
|
163
|
+
}
|
|
164
|
+
isMinuteValid(minute, selectedTime) {
|
|
165
|
+
const testTime = Object.assign(Object.assign({}, selectedTime), { minutes: minute });
|
|
166
|
+
return this.validateTime(testTime);
|
|
167
|
+
}
|
|
168
|
+
isSecondValid(second, selectedTime) {
|
|
169
|
+
const testTime = Object.assign(Object.assign({}, selectedTime), { seconds: second });
|
|
170
|
+
return this.validateTime(testTime);
|
|
171
|
+
}
|
|
172
|
+
// Private methods
|
|
173
|
+
renderLabel() {
|
|
174
|
+
if (!this.label)
|
|
175
|
+
return nothing;
|
|
176
|
+
return html `
|
|
177
|
+
<label class="time-picker__label" part="label" for="time-input">
|
|
178
|
+
${this.label}
|
|
179
|
+
${this.required ? html `<span class="time-picker__required">*</span>` : nothing}
|
|
180
|
+
</label>
|
|
181
|
+
`;
|
|
182
|
+
}
|
|
183
|
+
renderInput() {
|
|
184
|
+
const formatPlaceholder = this.getFormatPlaceholder();
|
|
185
|
+
return html `
|
|
186
|
+
<div class="time-picker__input-wrapper" part="input-wrapper">
|
|
187
|
+
<nr-input
|
|
188
|
+
id="time-input"
|
|
189
|
+
part="input"
|
|
190
|
+
type="calendar"
|
|
191
|
+
.value="${this.inputValue}"
|
|
192
|
+
placeholder="${this.placeholder || formatPlaceholder}"
|
|
193
|
+
?disabled="${this.disabled}"
|
|
194
|
+
?readonly="${false}"
|
|
195
|
+
?required="${this.required}"
|
|
196
|
+
.state="${this.state === TimePickerState.Error ? "error" /* INPUT_STATE.Error */ : "default" /* INPUT_STATE.Default */}"
|
|
197
|
+
@click="${this.handleInputClick}"
|
|
198
|
+
@nr-input="${this.handleInputChange}"
|
|
199
|
+
@nr-blur="${this.handleInputBlur}"
|
|
200
|
+
>
|
|
201
|
+
</nr-input>
|
|
202
|
+
</div>
|
|
203
|
+
`;
|
|
204
|
+
}
|
|
205
|
+
renderDropdown() {
|
|
206
|
+
if (!this.dropdownController.isOpen)
|
|
207
|
+
return nothing;
|
|
208
|
+
return html `
|
|
209
|
+
<div
|
|
210
|
+
class="time-picker__dropdown time-picker__dropdown--open"
|
|
211
|
+
part="dropdown"
|
|
212
|
+
@click="${this.handleDropdownClick}"
|
|
213
|
+
>
|
|
214
|
+
${this.renderColumnPicker()}
|
|
215
|
+
${this.renderActions()}
|
|
216
|
+
</div>
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
renderColumnPicker() {
|
|
220
|
+
const selectedTime = this.selectionController.getSelectedTime();
|
|
221
|
+
const config = this.getConfig();
|
|
222
|
+
return html `
|
|
223
|
+
<div class="time-picker__columns" part="columns">
|
|
224
|
+
${this.renderHourColumn(selectedTime, config)}
|
|
225
|
+
${this.renderMinuteColumn(selectedTime)}
|
|
226
|
+
${this.showSeconds ? this.renderSecondColumn(selectedTime) : nothing}
|
|
227
|
+
</div>
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
renderHourColumn(selectedTime, config) {
|
|
231
|
+
const hours = config.format === TimeFormat.TwelveHour
|
|
232
|
+
? Array.from({ length: 12 }, (_, i) => i === 0 ? 12 : i)
|
|
233
|
+
: Array.from({ length: 24 }, (_, i) => i);
|
|
234
|
+
const displayHour = selectedTime && config.format === TimeFormat.TwelveHour
|
|
235
|
+
? this.formattingController.formatHours(selectedTime.hours)
|
|
236
|
+
: selectedTime === null || selectedTime === void 0 ? void 0 : selectedTime.hours;
|
|
237
|
+
return html `
|
|
238
|
+
<div class="time-picker__column" part="hour-column">
|
|
239
|
+
<div class="time-picker__column-list">
|
|
240
|
+
${hours.map(hour => {
|
|
241
|
+
// Convert display hour to actual hour for validation
|
|
242
|
+
let actualHour = hour;
|
|
243
|
+
if (config.format === TimeFormat.TwelveHour && selectedTime) {
|
|
244
|
+
const currentPeriod = this.formattingController.getPeriod(selectedTime.hours);
|
|
245
|
+
if (hour === 12) {
|
|
246
|
+
actualHour = currentPeriod === TimePeriod.AM ? 0 : 12;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
actualHour = currentPeriod === TimePeriod.AM ? hour : hour + 12;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Use EMPTY_TIME_VALUE for validation when no time is selected
|
|
253
|
+
const timeForValidation = selectedTime || EMPTY_TIME_VALUE;
|
|
254
|
+
const isValid = this.isHourValid(actualHour, timeForValidation);
|
|
255
|
+
const isSelected = selectedTime ? hour === displayHour : false;
|
|
256
|
+
return html `
|
|
257
|
+
<div
|
|
258
|
+
class="time-picker__column-item ${isSelected ? 'time-picker__column-item--selected' : ''} ${!isValid ? 'time-picker__column-item--disabled' : ''}"
|
|
259
|
+
@click="${isValid ? () => this.handleHourSelect(hour, config.format) : null}"
|
|
260
|
+
>
|
|
261
|
+
${hour.toString().padStart(2, '0')}
|
|
262
|
+
</div>
|
|
263
|
+
`;
|
|
264
|
+
})}
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
`;
|
|
268
|
+
}
|
|
269
|
+
renderMinuteColumn(selectedTime) {
|
|
270
|
+
const minutes = Array.from({ length: 60 }, (_, i) => i);
|
|
271
|
+
return html `
|
|
272
|
+
<div class="time-picker__column" part="minute-column">
|
|
273
|
+
<div class="time-picker__column-list">
|
|
274
|
+
${minutes.map(minute => {
|
|
275
|
+
// Use EMPTY_TIME_VALUE for validation when no time is selected
|
|
276
|
+
const timeForValidation = selectedTime || EMPTY_TIME_VALUE;
|
|
277
|
+
const isValid = this.isMinuteValid(minute, timeForValidation);
|
|
278
|
+
const isSelected = selectedTime ? minute === selectedTime.minutes : false;
|
|
279
|
+
return html `
|
|
280
|
+
<div
|
|
281
|
+
class="time-picker__column-item ${isSelected ? 'time-picker__column-item--selected' : ''} ${!isValid ? 'time-picker__column-item--disabled' : ''}"
|
|
282
|
+
@click="${isValid ? () => this.handleMinuteSelect(minute) : null}"
|
|
283
|
+
>
|
|
284
|
+
${minute.toString().padStart(2, '0')}
|
|
285
|
+
</div>
|
|
286
|
+
`;
|
|
287
|
+
})}
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
renderSecondColumn(selectedTime) {
|
|
293
|
+
const seconds = Array.from({ length: 60 }, (_, i) => i);
|
|
294
|
+
return html `
|
|
295
|
+
<div class="time-picker__column" part="second-column">
|
|
296
|
+
<div class="time-picker__column-list">
|
|
297
|
+
${seconds.map(second => {
|
|
298
|
+
// Use EMPTY_TIME_VALUE for validation when no time is selected
|
|
299
|
+
const timeForValidation = selectedTime || EMPTY_TIME_VALUE;
|
|
300
|
+
const isValid = this.isSecondValid(second, timeForValidation);
|
|
301
|
+
const isSelected = selectedTime ? second === selectedTime.seconds : false;
|
|
302
|
+
return html `
|
|
303
|
+
<div
|
|
304
|
+
class="time-picker__column-item ${isSelected ? 'time-picker__column-item--selected' : ''} ${!isValid ? 'time-picker__column-item--disabled' : ''}"
|
|
305
|
+
@click="${isValid ? () => this.handleSecondSelect(second) : null}"
|
|
306
|
+
>
|
|
307
|
+
${second.toString().padStart(2, '0')}
|
|
308
|
+
</div>
|
|
309
|
+
`;
|
|
310
|
+
})}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
`;
|
|
314
|
+
}
|
|
315
|
+
renderActions() {
|
|
316
|
+
return html `
|
|
317
|
+
<div class="time-picker__actions">
|
|
318
|
+
<nr-button
|
|
319
|
+
type="ghost"
|
|
320
|
+
size="small"
|
|
321
|
+
@click="${() => this.setToNow()}"
|
|
322
|
+
>
|
|
323
|
+
Now
|
|
324
|
+
</nr-button>
|
|
325
|
+
<nr-button
|
|
326
|
+
type="primary"
|
|
327
|
+
size="small"
|
|
328
|
+
@click="${this.handleOkClick}"
|
|
329
|
+
>
|
|
330
|
+
OK
|
|
331
|
+
</nr-button>
|
|
332
|
+
</div>
|
|
333
|
+
`;
|
|
334
|
+
}
|
|
335
|
+
renderHelperText() {
|
|
336
|
+
const text = this.validationMessage || this.helperText;
|
|
337
|
+
if (!text)
|
|
338
|
+
return nothing;
|
|
339
|
+
const isError = this.state === TimePickerState.Error || !!this.validationMessage;
|
|
340
|
+
return html `
|
|
341
|
+
<div class="time-picker__helper-text ${isError ? 'time-picker__helper-text--error' : ''}" part="helper-text">
|
|
342
|
+
${text}
|
|
343
|
+
</div>
|
|
344
|
+
`;
|
|
345
|
+
}
|
|
346
|
+
scrollToSelectedTime() {
|
|
347
|
+
try {
|
|
348
|
+
const selectedTime = this.selectionController.getSelectedTime();
|
|
349
|
+
if (!selectedTime)
|
|
350
|
+
return;
|
|
351
|
+
// Scroll each column to the selected value
|
|
352
|
+
this.scrollToSelectedHour(selectedTime);
|
|
353
|
+
this.scrollToSelectedMinute(selectedTime);
|
|
354
|
+
if (this.showSeconds) {
|
|
355
|
+
this.scrollToSelectedSecond(selectedTime);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
console.warn('Failed to scroll to selected time:', error);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
scrollToSelectedHour(selectedTime) {
|
|
363
|
+
var _a;
|
|
364
|
+
const config = this.getConfig();
|
|
365
|
+
const hourColumn = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.time-picker__column:first-child .time-picker__column-list');
|
|
366
|
+
if (!hourColumn)
|
|
367
|
+
return;
|
|
368
|
+
let displayHour;
|
|
369
|
+
if (config.format === TimeFormat.TwelveHour) {
|
|
370
|
+
// Convert 24-hour to 12-hour format
|
|
371
|
+
if (selectedTime.hours === 0 || selectedTime.hours === 12) {
|
|
372
|
+
displayHour = 12;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
displayHour = selectedTime.hours > 12 ? selectedTime.hours - 12 : selectedTime.hours;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
displayHour = selectedTime.hours;
|
|
380
|
+
}
|
|
381
|
+
// Find the selected hour element
|
|
382
|
+
const selectedHourElement = hourColumn.querySelector(`.time-picker__column-item:nth-child(${this.getHourIndex(displayHour, config.format) + 1})`);
|
|
383
|
+
if (selectedHourElement) {
|
|
384
|
+
selectedHourElement.scrollIntoView({
|
|
385
|
+
behavior: this.scrollBehavior,
|
|
386
|
+
block: 'center',
|
|
387
|
+
inline: 'nearest'
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
scrollToSelectedMinute(selectedTime) {
|
|
392
|
+
var _a;
|
|
393
|
+
const minuteColumn = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.time-picker__column:nth-child(2) .time-picker__column-list');
|
|
394
|
+
if (!minuteColumn)
|
|
395
|
+
return;
|
|
396
|
+
// Find the selected minute element (minute + 1 because nth-child is 1-indexed)
|
|
397
|
+
const selectedMinuteElement = minuteColumn.querySelector(`.time-picker__column-item:nth-child(${selectedTime.minutes + 1})`);
|
|
398
|
+
if (selectedMinuteElement) {
|
|
399
|
+
selectedMinuteElement.scrollIntoView({
|
|
400
|
+
behavior: this.scrollBehavior,
|
|
401
|
+
block: 'center',
|
|
402
|
+
inline: 'nearest'
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
scrollToSelectedSecond(selectedTime) {
|
|
407
|
+
var _a;
|
|
408
|
+
const secondColumn = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.time-picker__column:nth-child(3) .time-picker__column-list');
|
|
409
|
+
if (!secondColumn)
|
|
410
|
+
return;
|
|
411
|
+
// Find the selected second element (second + 1 because nth-child is 1-indexed)
|
|
412
|
+
const selectedSecondElement = secondColumn.querySelector(`.time-picker__column-item:nth-child(${selectedTime.seconds + 1})`);
|
|
413
|
+
if (selectedSecondElement) {
|
|
414
|
+
selectedSecondElement.scrollIntoView({
|
|
415
|
+
behavior: this.scrollBehavior,
|
|
416
|
+
block: 'center',
|
|
417
|
+
inline: 'nearest'
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
getHourIndex(displayHour, format) {
|
|
422
|
+
if (format === TimeFormat.TwelveHour) {
|
|
423
|
+
// For 12-hour format: 12, 1, 2, ..., 11
|
|
424
|
+
return displayHour === 12 ? 0 : displayHour;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
// For 24-hour format: 0, 1, 2, ..., 23
|
|
428
|
+
return displayHour;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Event handlers
|
|
432
|
+
handleComponentClick(e) {
|
|
433
|
+
// Stop propagation to prevent document click handler from firing
|
|
434
|
+
e.stopPropagation();
|
|
435
|
+
}
|
|
436
|
+
handleDocumentClick(e) {
|
|
437
|
+
var _a;
|
|
438
|
+
// Close dropdown when clicking outside the component
|
|
439
|
+
if (this.dropdownController.isOpen) {
|
|
440
|
+
const target = e.target;
|
|
441
|
+
const isClickInsideComponent = this.contains(target) ||
|
|
442
|
+
((_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(target));
|
|
443
|
+
if (!isClickInsideComponent) {
|
|
444
|
+
this.dropdownController.close();
|
|
445
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.BLUR, {
|
|
446
|
+
bubbles: true,
|
|
447
|
+
composed: true,
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
handleDropdownClick(e) {
|
|
453
|
+
// Prevent dropdown from closing when clicking inside
|
|
454
|
+
e.stopPropagation();
|
|
455
|
+
}
|
|
456
|
+
handleOkClick() {
|
|
457
|
+
// Close the dropdown and emit final change event
|
|
458
|
+
this.dropdownController.close();
|
|
459
|
+
const selectedTime = this.selectionController.getSelectedTime();
|
|
460
|
+
if (selectedTime) {
|
|
461
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.TIME_CHANGE, {
|
|
462
|
+
bubbles: true,
|
|
463
|
+
composed: true,
|
|
464
|
+
detail: { value: this.value, time: selectedTime }
|
|
465
|
+
}));
|
|
466
|
+
}
|
|
467
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.BLUR, {
|
|
468
|
+
bubbles: true,
|
|
469
|
+
composed: true,
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
handleInputBlur() {
|
|
473
|
+
// Only close dropdown if clicking outside the component
|
|
474
|
+
setTimeout(() => {
|
|
475
|
+
var _a;
|
|
476
|
+
const activeElement = document.activeElement;
|
|
477
|
+
const isWithinComponent = this.contains(activeElement) ||
|
|
478
|
+
((_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(activeElement));
|
|
479
|
+
if (!isWithinComponent) {
|
|
480
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.BLUR, {
|
|
481
|
+
bubbles: true,
|
|
482
|
+
composed: true,
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
}, 150);
|
|
486
|
+
}
|
|
487
|
+
handleInputClick(e) {
|
|
488
|
+
e.preventDefault();
|
|
489
|
+
e.stopPropagation();
|
|
490
|
+
if (!this.disabled) {
|
|
491
|
+
// Only open if closed - don't close when clicking input
|
|
492
|
+
if (!this.dropdownController.isOpen) {
|
|
493
|
+
this.dropdownController.open();
|
|
494
|
+
// Scroll to selected items when dropdown opens
|
|
495
|
+
setTimeout(() => {
|
|
496
|
+
this.scrollToSelectedTime();
|
|
497
|
+
}, 50);
|
|
498
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.FOCUS, {
|
|
499
|
+
bubbles: true,
|
|
500
|
+
composed: true,
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
handleInputChange(e) {
|
|
506
|
+
var _a;
|
|
507
|
+
if (this.disabled)
|
|
508
|
+
return;
|
|
509
|
+
const inputValue = ((_a = e.detail) === null || _a === void 0 ? void 0 : _a.value) || '';
|
|
510
|
+
this.inputValue = inputValue;
|
|
511
|
+
// Parse the input value and update the time selection
|
|
512
|
+
const parsedTime = this.formattingController.parseInputValue(inputValue);
|
|
513
|
+
if (parsedTime) {
|
|
514
|
+
// Validate the parsed time
|
|
515
|
+
if (this.validateTime(parsedTime)) {
|
|
516
|
+
this.selectionController.selectTime(parsedTime);
|
|
517
|
+
this.value = this.formattingController.formatForInput(parsedTime);
|
|
518
|
+
this.state = TimePickerState.Default;
|
|
519
|
+
// Scroll to the newly selected time if dropdown is open
|
|
520
|
+
if (this.dropdownController.isOpen) {
|
|
521
|
+
setTimeout(() => {
|
|
522
|
+
this.scrollToSelectedTime();
|
|
523
|
+
}, 10);
|
|
524
|
+
}
|
|
525
|
+
// Emit change event
|
|
526
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.TIME_CHANGE, {
|
|
527
|
+
bubbles: true,
|
|
528
|
+
composed: true,
|
|
529
|
+
detail: { value: this.value, time: parsedTime }
|
|
530
|
+
}));
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
// Invalid time - show error state but don't clear the input
|
|
534
|
+
this.state = TimePickerState.Error;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
else if (inputValue === '') {
|
|
538
|
+
// Empty input - clear the selection
|
|
539
|
+
this.selectionController.clearSelection();
|
|
540
|
+
this.value = '';
|
|
541
|
+
this.state = TimePickerState.Default;
|
|
542
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.TIME_CHANGE, {
|
|
543
|
+
bubbles: true,
|
|
544
|
+
composed: true,
|
|
545
|
+
detail: { value: '', time: null }
|
|
546
|
+
}));
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
// Invalid format - show error state
|
|
550
|
+
this.state = TimePickerState.Error;
|
|
551
|
+
}
|
|
552
|
+
// Request update to re-render with new state
|
|
553
|
+
this.requestUpdate();
|
|
554
|
+
}
|
|
555
|
+
handleHourSelect(hour, format) {
|
|
556
|
+
const selectedTime = this.selectionController.getSelectedTime() || TimeUtils.getCurrentTime();
|
|
557
|
+
let adjustedHour = hour;
|
|
558
|
+
if (format === TimeFormat.TwelveHour) {
|
|
559
|
+
const currentPeriod = this.formattingController.getPeriod(selectedTime.hours);
|
|
560
|
+
if (hour === 12) {
|
|
561
|
+
adjustedHour = currentPeriod === TimePeriod.AM ? 0 : 12;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
adjustedHour = currentPeriod === TimePeriod.AM ? hour : hour + 12;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const updatedTime = Object.assign(Object.assign({}, selectedTime), { hours: adjustedHour });
|
|
568
|
+
if (this.validateTime(updatedTime)) {
|
|
569
|
+
this.selectionController.selectTime(updatedTime);
|
|
570
|
+
this.updateInputValue();
|
|
571
|
+
// No scrolling when clicking on individual items
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
handleMinuteSelect(minute) {
|
|
575
|
+
const selectedTime = this.selectionController.getSelectedTime() || TimeUtils.getCurrentTime();
|
|
576
|
+
const updatedTime = Object.assign(Object.assign({}, selectedTime), { minutes: minute });
|
|
577
|
+
if (this.validateTime(updatedTime)) {
|
|
578
|
+
this.selectionController.selectTime(updatedTime);
|
|
579
|
+
this.updateInputValue();
|
|
580
|
+
// No scrolling when clicking on individual items
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
handleSecondSelect(second) {
|
|
584
|
+
const selectedTime = this.selectionController.getSelectedTime() || TimeUtils.getCurrentTime();
|
|
585
|
+
const updatedTime = Object.assign(Object.assign({}, selectedTime), { seconds: second });
|
|
586
|
+
if (this.validateTime(updatedTime)) {
|
|
587
|
+
this.selectionController.selectTime(updatedTime);
|
|
588
|
+
this.updateInputValue();
|
|
589
|
+
// No scrolling when clicking on individual items
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
// Utility methods
|
|
593
|
+
shouldUpdateConstraints(changedProperties) {
|
|
594
|
+
return (changedProperties.has('minTime') ||
|
|
595
|
+
changedProperties.has('maxTime') ||
|
|
596
|
+
changedProperties.has('disabledTimes') ||
|
|
597
|
+
changedProperties.has('enabledTimes'));
|
|
598
|
+
}
|
|
599
|
+
updateConstraints() {
|
|
600
|
+
const constraints = {
|
|
601
|
+
minTime: this.minTime,
|
|
602
|
+
maxTime: this.maxTime,
|
|
603
|
+
disabledTimes: this.disabledTimes || [],
|
|
604
|
+
enabledTimes: this.enabledTimes,
|
|
605
|
+
};
|
|
606
|
+
this.validationController.setConstraints(constraints);
|
|
607
|
+
}
|
|
608
|
+
setTimeFromValue(value) {
|
|
609
|
+
if (this.selectionController.setTimeFromString(value)) {
|
|
610
|
+
this.inputValue = value;
|
|
611
|
+
this.requestUpdate();
|
|
612
|
+
// Scroll to the time when setting from value (if dropdown is open)
|
|
613
|
+
if (this.dropdownController.isOpen) {
|
|
614
|
+
setTimeout(() => {
|
|
615
|
+
this.scrollToSelectedTime();
|
|
616
|
+
}, 50);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
updateInputValue() {
|
|
621
|
+
const selectedTime = this.selectionController.getSelectedTime();
|
|
622
|
+
if (selectedTime) {
|
|
623
|
+
const formattedValue = this.formattingController.formatForDisplay(selectedTime);
|
|
624
|
+
this.inputValue = formattedValue;
|
|
625
|
+
this.value = formattedValue;
|
|
626
|
+
this.dispatchEvent(new CustomEvent(TIME_PICKER_EVENTS.TIME_CHANGE, {
|
|
627
|
+
detail: { value: formattedValue, time: selectedTime },
|
|
628
|
+
bubbles: true,
|
|
629
|
+
composed: true,
|
|
630
|
+
}));
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
getConfig() {
|
|
634
|
+
return {
|
|
635
|
+
format: this.format,
|
|
636
|
+
showSeconds: this.showSeconds,
|
|
637
|
+
step: {
|
|
638
|
+
hours: TimeStep.One,
|
|
639
|
+
minutes: TimeStep.One,
|
|
640
|
+
seconds: TimeStep.One,
|
|
641
|
+
},
|
|
642
|
+
use12HourClock: this.format === TimeFormat.TwelveHour,
|
|
643
|
+
minuteInterval: 1,
|
|
644
|
+
secondInterval: 1,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
// TimePickerHost interface implementation
|
|
648
|
+
getCurrentTime() {
|
|
649
|
+
return this.selectionController.getSelectedTime() || EMPTY_TIME_VALUE;
|
|
650
|
+
}
|
|
651
|
+
setTime(time) {
|
|
652
|
+
this.selectionController.selectTime(time);
|
|
653
|
+
this.updateInputValue();
|
|
654
|
+
// Scroll to the newly selected time if dropdown is open
|
|
655
|
+
if (this.dropdownController.isOpen) {
|
|
656
|
+
setTimeout(() => {
|
|
657
|
+
this.scrollToSelectedTime();
|
|
658
|
+
}, 10);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
formatTime(time) {
|
|
662
|
+
return this.formattingController.formatForDisplay(time);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Get appropriate placeholder text based on format
|
|
666
|
+
*/
|
|
667
|
+
getFormatPlaceholder() {
|
|
668
|
+
if (this.format === TimeFormat.TwelveHour) {
|
|
669
|
+
return this.showSeconds ? 'HH:MM:SS AM/PM' : 'HH:MM AM/PM';
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
return this.showSeconds ? 'HH:MM:SS' : 'HH:MM';
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
parseTime(timeString) {
|
|
676
|
+
return this.formattingController.parseInputValue(timeString);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
NrTimePickerElement.styles = [timePickerStyles];
|
|
680
|
+
__decorate([
|
|
681
|
+
property({ type: String })
|
|
682
|
+
], NrTimePickerElement.prototype, "value", void 0);
|
|
683
|
+
__decorate([
|
|
684
|
+
property({ type: String })
|
|
685
|
+
], NrTimePickerElement.prototype, "name", void 0);
|
|
686
|
+
__decorate([
|
|
687
|
+
property({ type: String })
|
|
688
|
+
], NrTimePickerElement.prototype, "placeholder", void 0);
|
|
689
|
+
__decorate([
|
|
690
|
+
property({ type: String })
|
|
691
|
+
], NrTimePickerElement.prototype, "format", void 0);
|
|
692
|
+
__decorate([
|
|
693
|
+
property({ type: Boolean, attribute: 'show-seconds' })
|
|
694
|
+
], NrTimePickerElement.prototype, "showSeconds", void 0);
|
|
695
|
+
__decorate([
|
|
696
|
+
property({ type: Boolean })
|
|
697
|
+
], NrTimePickerElement.prototype, "disabled", void 0);
|
|
698
|
+
__decorate([
|
|
699
|
+
property({ type: Boolean })
|
|
700
|
+
], NrTimePickerElement.prototype, "readonly", void 0);
|
|
701
|
+
__decorate([
|
|
702
|
+
property({ type: Boolean })
|
|
703
|
+
], NrTimePickerElement.prototype, "required", void 0);
|
|
704
|
+
__decorate([
|
|
705
|
+
property({ type: String, attribute: 'min-time' })
|
|
706
|
+
], NrTimePickerElement.prototype, "minTime", void 0);
|
|
707
|
+
__decorate([
|
|
708
|
+
property({ type: String, attribute: 'max-time' })
|
|
709
|
+
], NrTimePickerElement.prototype, "maxTime", void 0);
|
|
710
|
+
__decorate([
|
|
711
|
+
property({ type: Array, attribute: 'disabled-times' })
|
|
712
|
+
], NrTimePickerElement.prototype, "disabledTimes", void 0);
|
|
713
|
+
__decorate([
|
|
714
|
+
property({ type: Array, attribute: 'enabled-times' })
|
|
715
|
+
], NrTimePickerElement.prototype, "enabledTimes", void 0);
|
|
716
|
+
__decorate([
|
|
717
|
+
property({ type: String, attribute: 'helper-text' })
|
|
718
|
+
], NrTimePickerElement.prototype, "helperText", void 0);
|
|
719
|
+
__decorate([
|
|
720
|
+
property({ type: String })
|
|
721
|
+
], NrTimePickerElement.prototype, "label", void 0);
|
|
722
|
+
__decorate([
|
|
723
|
+
property({ type: String })
|
|
724
|
+
], NrTimePickerElement.prototype, "size", void 0);
|
|
725
|
+
__decorate([
|
|
726
|
+
property({ type: String })
|
|
727
|
+
], NrTimePickerElement.prototype, "variant", void 0);
|
|
728
|
+
__decorate([
|
|
729
|
+
property({ type: String })
|
|
730
|
+
], NrTimePickerElement.prototype, "placement", void 0);
|
|
731
|
+
__decorate([
|
|
732
|
+
property({ type: String, attribute: 'scroll-behavior' })
|
|
733
|
+
], NrTimePickerElement.prototype, "scrollBehavior", void 0);
|
|
734
|
+
__decorate([
|
|
735
|
+
state()
|
|
736
|
+
], NrTimePickerElement.prototype, "inputValue", void 0);
|
|
737
|
+
__decorate([
|
|
738
|
+
state()
|
|
739
|
+
], NrTimePickerElement.prototype, "state", void 0);
|
|
740
|
+
__decorate([
|
|
741
|
+
state()
|
|
742
|
+
], NrTimePickerElement.prototype, "validationMessage", void 0);
|
|
743
|
+
NrTimePickerElement = __decorate([
|
|
744
|
+
customElement('nr-timepicker')
|
|
745
|
+
], NrTimePickerElement);
|
|
746
|
+
export { NrTimePickerElement };
|
|
747
|
+
//# sourceMappingURL=timepicker.component.js.map
|