@vaadin/time-picker 24.6.0-alpha4 → 24.6.0-alpha6
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/package.json +14 -12
- package/src/vaadin-lit-time-picker-combo-box.js +113 -0
- package/src/vaadin-lit-time-picker-item.js +50 -0
- package/src/vaadin-lit-time-picker-overlay.js +76 -0
- package/src/vaadin-lit-time-picker-scroller.js +59 -0
- package/src/vaadin-lit-time-picker.js +161 -0
- package/src/vaadin-time-picker-helper.d.ts +32 -0
- package/src/vaadin-time-picker-helper.js +91 -0
- package/src/vaadin-time-picker-mixin.d.ts +159 -0
- package/src/vaadin-time-picker-mixin.js +605 -0
- package/src/vaadin-time-picker.d.ts +4 -121
- package/src/vaadin-time-picker.js +3 -655
- package/theme/lumo/vaadin-lit-time-picker.d.ts +7 -0
- package/theme/lumo/vaadin-lit-time-picker.js +7 -0
- package/theme/material/vaadin-lit-time-picker.d.ts +7 -0
- package/theme/material/vaadin-lit-time-picker.js +7 -0
- package/vaadin-lit-time-picker.d.ts +1 -0
- package/vaadin-lit-time-picker.js +2 -0
- package/web-types.json +6 -6
- package/web-types.lit.json +8 -8
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2018 - 2024 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
7
|
+
import { PatternMixin } from '@vaadin/field-base/src/pattern-mixin.js';
|
|
8
|
+
import { formatISOTime, parseISOTime, validateTime } from './vaadin-time-picker-helper.js';
|
|
9
|
+
|
|
10
|
+
const MIN_ALLOWED_TIME = '00:00:00.000';
|
|
11
|
+
const MAX_ALLOWED_TIME = '23:59:59.999';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A mixin providing common time-picker functionality.
|
|
15
|
+
*
|
|
16
|
+
* @polymerMixin
|
|
17
|
+
* @mixes InputControlMixin
|
|
18
|
+
* @mixes PatternMixin
|
|
19
|
+
*/
|
|
20
|
+
export const TimePickerMixin = (superClass) =>
|
|
21
|
+
class TimePickerMixinClass extends PatternMixin(InputControlMixin(superClass)) {
|
|
22
|
+
static get properties() {
|
|
23
|
+
return {
|
|
24
|
+
/**
|
|
25
|
+
* The time value for this element.
|
|
26
|
+
*
|
|
27
|
+
* Supported time formats are in ISO 8601:
|
|
28
|
+
* - `hh:mm` (default)
|
|
29
|
+
* - `hh:mm:ss`
|
|
30
|
+
* - `hh:mm:ss.fff`
|
|
31
|
+
* @type {string}
|
|
32
|
+
*/
|
|
33
|
+
value: {
|
|
34
|
+
type: String,
|
|
35
|
+
notify: true,
|
|
36
|
+
value: '',
|
|
37
|
+
sync: true,
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* True if the dropdown is open, false otherwise.
|
|
42
|
+
*/
|
|
43
|
+
opened: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
notify: true,
|
|
46
|
+
value: false,
|
|
47
|
+
reflectToAttribute: true,
|
|
48
|
+
sync: true,
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Minimum time allowed.
|
|
53
|
+
*
|
|
54
|
+
* Supported time formats are in ISO 8601:
|
|
55
|
+
* - `hh:mm`
|
|
56
|
+
* - `hh:mm:ss`
|
|
57
|
+
* - `hh:mm:ss.fff`
|
|
58
|
+
* @type {string}
|
|
59
|
+
*/
|
|
60
|
+
min: {
|
|
61
|
+
type: String,
|
|
62
|
+
value: '',
|
|
63
|
+
sync: true,
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Maximum time allowed.
|
|
68
|
+
*
|
|
69
|
+
* Supported time formats are in ISO 8601:
|
|
70
|
+
* - `hh:mm`
|
|
71
|
+
* - `hh:mm:ss`
|
|
72
|
+
* - `hh:mm:ss.fff`
|
|
73
|
+
* @type {string}
|
|
74
|
+
*/
|
|
75
|
+
max: {
|
|
76
|
+
type: String,
|
|
77
|
+
value: '',
|
|
78
|
+
sync: true,
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Defines the time interval (in seconds) between the items displayed
|
|
83
|
+
* in the time selection box. The default is 1 hour (i.e. `3600`).
|
|
84
|
+
*
|
|
85
|
+
* It also configures the precision of the value string. By default
|
|
86
|
+
* the component formats values as `hh:mm` but setting a step value
|
|
87
|
+
* lower than one minute or one second, format resolution changes to
|
|
88
|
+
* `hh:mm:ss` and `hh:mm:ss.fff` respectively.
|
|
89
|
+
*
|
|
90
|
+
* Unit must be set in seconds, and for correctly configuring intervals
|
|
91
|
+
* in the dropdown, it need to evenly divide a day.
|
|
92
|
+
*
|
|
93
|
+
* Note: it is possible to define step that is dividing an hour in inexact
|
|
94
|
+
* fragments (i.e. 5760 seconds which equals 1 hour 36 minutes), but it is
|
|
95
|
+
* not recommended to use it for better UX experience.
|
|
96
|
+
*/
|
|
97
|
+
step: {
|
|
98
|
+
type: Number,
|
|
99
|
+
sync: true,
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Set true to prevent the overlay from opening automatically.
|
|
104
|
+
* @attr {boolean} auto-open-disabled
|
|
105
|
+
*/
|
|
106
|
+
autoOpenDisabled: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
sync: true,
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A space-delimited list of CSS class names to set on the overlay element.
|
|
113
|
+
*
|
|
114
|
+
* @attr {string} overlay-class
|
|
115
|
+
*/
|
|
116
|
+
overlayClass: {
|
|
117
|
+
type: String,
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The object used to localize this component.
|
|
122
|
+
* To change the default localization, replace the entire
|
|
123
|
+
* _i18n_ object or just the property you want to modify.
|
|
124
|
+
*
|
|
125
|
+
* The object has the following JSON structure:
|
|
126
|
+
*
|
|
127
|
+
* ```
|
|
128
|
+
* {
|
|
129
|
+
* // A function to format given `Object` as
|
|
130
|
+
* // time string. Object is in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
131
|
+
* formatTime: (time) => {
|
|
132
|
+
* // returns a string representation of the given
|
|
133
|
+
* // object in `hh` / 'hh:mm' / 'hh:mm:ss' / 'hh:mm:ss.fff' - formats
|
|
134
|
+
* },
|
|
135
|
+
*
|
|
136
|
+
* // A function to parse the given text to an `Object` in the format
|
|
137
|
+
* // `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`.
|
|
138
|
+
* // Must properly parse (at least) text
|
|
139
|
+
* // formatted by `formatTime`.
|
|
140
|
+
* parseTime: text => {
|
|
141
|
+
* // Parses a string in object/string that can be formatted by`formatTime`.
|
|
142
|
+
* }
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* Both `formatTime` and `parseTime` need to be implemented
|
|
147
|
+
* to ensure the component works properly.
|
|
148
|
+
*
|
|
149
|
+
* @type {!TimePickerI18n}
|
|
150
|
+
*/
|
|
151
|
+
i18n: {
|
|
152
|
+
type: Object,
|
|
153
|
+
sync: true,
|
|
154
|
+
value: () => {
|
|
155
|
+
return {
|
|
156
|
+
formatTime: formatISOTime,
|
|
157
|
+
parseTime: parseISOTime,
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/** @private */
|
|
163
|
+
_comboBoxValue: {
|
|
164
|
+
type: String,
|
|
165
|
+
sync: true,
|
|
166
|
+
observer: '__comboBoxValueChanged',
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/** @private */
|
|
170
|
+
__dropdownItems: {
|
|
171
|
+
type: Array,
|
|
172
|
+
sync: true,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static get observers() {
|
|
178
|
+
return [
|
|
179
|
+
'__updateAriaAttributes(__dropdownItems, opened, inputElement)',
|
|
180
|
+
'__updateDropdownItems(i18n, min, max, step)',
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static get constraints() {
|
|
185
|
+
return [...super.constraints, 'min', 'max'];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Used by `ClearButtonMixin` as a reference to the clear button element.
|
|
190
|
+
* @protected
|
|
191
|
+
* @return {!HTMLElement}
|
|
192
|
+
*/
|
|
193
|
+
get clearElement() {
|
|
194
|
+
return this.$.clearButton;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The input element's value when it cannot be parsed as a time, and an empty string otherwise.
|
|
199
|
+
*
|
|
200
|
+
* @private
|
|
201
|
+
* @return {string}
|
|
202
|
+
*/
|
|
203
|
+
get __unparsableValue() {
|
|
204
|
+
if (this._inputElementValue && !this.i18n.parseTime(this._inputElementValue)) {
|
|
205
|
+
return this._inputElementValue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Override method inherited from `InputMixin` to forward the input to combo-box.
|
|
213
|
+
* @protected
|
|
214
|
+
* @override
|
|
215
|
+
*/
|
|
216
|
+
_inputElementChanged(input) {
|
|
217
|
+
super._inputElementChanged(input);
|
|
218
|
+
|
|
219
|
+
if (input) {
|
|
220
|
+
this.$.comboBox._setInputElement(input);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Opens the dropdown list.
|
|
226
|
+
*/
|
|
227
|
+
open() {
|
|
228
|
+
if (!this.disabled && !this.readonly) {
|
|
229
|
+
this.opened = true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Closes the dropdown list.
|
|
235
|
+
*/
|
|
236
|
+
close() {
|
|
237
|
+
this.opened = false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Returns true if the current input value satisfies all constraints (if any).
|
|
242
|
+
* You can override this method for custom validations.
|
|
243
|
+
*
|
|
244
|
+
* @return {boolean} True if the value is valid
|
|
245
|
+
*/
|
|
246
|
+
checkValidity() {
|
|
247
|
+
return !!(
|
|
248
|
+
this.inputElement.checkValidity() &&
|
|
249
|
+
(!this.value || this._timeAllowed(this.i18n.parseTime(this.value))) &&
|
|
250
|
+
(!this._comboBoxValue || this.i18n.parseTime(this._comboBoxValue))
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @param {boolean} focused
|
|
256
|
+
* @override
|
|
257
|
+
* @protected
|
|
258
|
+
*/
|
|
259
|
+
_setFocused(focused) {
|
|
260
|
+
super._setFocused(focused);
|
|
261
|
+
|
|
262
|
+
if (!focused) {
|
|
263
|
+
// Do not validate when focusout is caused by document
|
|
264
|
+
// losing focus, which happens on browser tab switch.
|
|
265
|
+
if (document.hasFocus()) {
|
|
266
|
+
this.validate();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** @private */
|
|
272
|
+
__validDayDivisor(step) {
|
|
273
|
+
// Valid if undefined, or exact divides a day, or has millisecond resolution
|
|
274
|
+
return !step || (24 * 3600) % step === 0 || (step < 1 && ((step % 1) * 1000) % 1 === 0);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Override an event listener from `KeyboardMixin`.
|
|
279
|
+
* @param {!KeyboardEvent} e
|
|
280
|
+
* @protected
|
|
281
|
+
*/
|
|
282
|
+
_onKeyDown(e) {
|
|
283
|
+
super._onKeyDown(e);
|
|
284
|
+
|
|
285
|
+
if (this.readonly || this.disabled || this.__dropdownItems.length) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const stepResolution = (this.__validDayDivisor(this.step) && this.step) || 60;
|
|
290
|
+
|
|
291
|
+
if (e.keyCode === 40) {
|
|
292
|
+
this.__onArrowPressWithStep(-stepResolution);
|
|
293
|
+
} else if (e.keyCode === 38) {
|
|
294
|
+
this.__onArrowPressWithStep(stepResolution);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Override an event listener from `KeyboardMixin`.
|
|
300
|
+
* Do not call `super` in order to override clear
|
|
301
|
+
* button logic defined in `InputControlMixin`.
|
|
302
|
+
* @param {Event} event
|
|
303
|
+
* @protected
|
|
304
|
+
*/
|
|
305
|
+
_onEscape() {
|
|
306
|
+
// Do nothing, the internal combo-box handles Escape.
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** @private */
|
|
310
|
+
__onArrowPressWithStep(step) {
|
|
311
|
+
const objWithStep = this.__addStep(this.__getMsec(this.__memoValue), step, true);
|
|
312
|
+
this.__memoValue = objWithStep;
|
|
313
|
+
|
|
314
|
+
// Setting `_comboBoxValue` property triggers the synchronous
|
|
315
|
+
// observer where the value can be parsed again, so we set
|
|
316
|
+
// this flag to ensure it does not alter the value.
|
|
317
|
+
this.__useMemo = true;
|
|
318
|
+
this._comboBoxValue = this.i18n.formatTime(objWithStep);
|
|
319
|
+
this.__useMemo = false;
|
|
320
|
+
|
|
321
|
+
this.__commitValueChange();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Depending on the nature of the value change that has occurred since
|
|
326
|
+
* the last commit attempt, triggers validation and fires an event:
|
|
327
|
+
*
|
|
328
|
+
* Value change | Event
|
|
329
|
+
* -------------------------|-------------------
|
|
330
|
+
* empty => parsable | change
|
|
331
|
+
* empty => unparsable | unparsable-change
|
|
332
|
+
* parsable => empty | change
|
|
333
|
+
* parsable => parsable | change
|
|
334
|
+
* parsable => unparsable | change
|
|
335
|
+
* unparsable => empty | unparsable-change
|
|
336
|
+
* unparsable => parsable | change
|
|
337
|
+
* unparsable => unparsable | unparsable-change
|
|
338
|
+
*
|
|
339
|
+
* @private
|
|
340
|
+
*/
|
|
341
|
+
__commitValueChange() {
|
|
342
|
+
const unparsableValue = this.__unparsableValue;
|
|
343
|
+
|
|
344
|
+
if (this.__committedValue !== this.value) {
|
|
345
|
+
this.validate();
|
|
346
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
|
347
|
+
} else if (this.__committedUnparsableValue !== unparsableValue) {
|
|
348
|
+
this.validate();
|
|
349
|
+
this.dispatchEvent(new CustomEvent('unparsable-change'));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this.__committedValue = this.value;
|
|
353
|
+
this.__committedUnparsableValue = unparsableValue;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Returning milliseconds from Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
358
|
+
* @private
|
|
359
|
+
*/
|
|
360
|
+
__getMsec(obj) {
|
|
361
|
+
let result = ((obj && obj.hours) || 0) * 60 * 60 * 1000;
|
|
362
|
+
result += ((obj && obj.minutes) || 0) * 60 * 1000;
|
|
363
|
+
result += ((obj && obj.seconds) || 0) * 1000;
|
|
364
|
+
result += (obj && parseInt(obj.milliseconds)) || 0;
|
|
365
|
+
|
|
366
|
+
return result;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Returning seconds from Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
371
|
+
* @private
|
|
372
|
+
*/
|
|
373
|
+
__getSec(obj) {
|
|
374
|
+
let result = ((obj && obj.hours) || 0) * 60 * 60;
|
|
375
|
+
result += ((obj && obj.minutes) || 0) * 60;
|
|
376
|
+
result += (obj && obj.seconds) || 0;
|
|
377
|
+
result += (obj && obj.milliseconds / 1000) || 0;
|
|
378
|
+
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Returning Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
384
|
+
* from the result of adding step value in milliseconds to the milliseconds amount.
|
|
385
|
+
* With `precision` parameter rounding the value to the closest step valid interval.
|
|
386
|
+
* @private
|
|
387
|
+
*/
|
|
388
|
+
__addStep(msec, step, precision) {
|
|
389
|
+
// If the time is `00:00` and step changes value downwards, it should be considered as `24:00`
|
|
390
|
+
if (msec === 0 && step < 0) {
|
|
391
|
+
msec = 24 * 60 * 60 * 1000;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const stepMsec = step * 1000;
|
|
395
|
+
const diffToNext = msec % stepMsec;
|
|
396
|
+
if (stepMsec < 0 && diffToNext && precision) {
|
|
397
|
+
msec -= diffToNext;
|
|
398
|
+
} else if (stepMsec > 0 && diffToNext && precision) {
|
|
399
|
+
msec -= diffToNext - stepMsec;
|
|
400
|
+
} else {
|
|
401
|
+
msec += stepMsec;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const hh = Math.floor(msec / 1000 / 60 / 60);
|
|
405
|
+
msec -= hh * 1000 * 60 * 60;
|
|
406
|
+
const mm = Math.floor(msec / 1000 / 60);
|
|
407
|
+
msec -= mm * 1000 * 60;
|
|
408
|
+
const ss = Math.floor(msec / 1000);
|
|
409
|
+
msec -= ss * 1000;
|
|
410
|
+
|
|
411
|
+
return { hours: hh < 24 ? hh : 0, minutes: mm, seconds: ss, milliseconds: msec };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** @private */
|
|
415
|
+
__updateDropdownItems(i18n, min, max, step) {
|
|
416
|
+
const minTimeObj = validateTime(parseISOTime(min || MIN_ALLOWED_TIME), step);
|
|
417
|
+
const minSec = this.__getSec(minTimeObj);
|
|
418
|
+
|
|
419
|
+
const maxTimeObj = validateTime(parseISOTime(max || MAX_ALLOWED_TIME), step);
|
|
420
|
+
const maxSec = this.__getSec(maxTimeObj);
|
|
421
|
+
|
|
422
|
+
this.__dropdownItems = this.__generateDropdownList(minSec, maxSec, step);
|
|
423
|
+
|
|
424
|
+
if (step !== this.__oldStep) {
|
|
425
|
+
this.__oldStep = step;
|
|
426
|
+
const parsedObj = validateTime(parseISOTime(this.value), step);
|
|
427
|
+
this.__updateValue(parsedObj);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (this.value) {
|
|
431
|
+
this._comboBoxValue = i18n.formatTime(i18n.parseTime(this.value));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** @private */
|
|
436
|
+
__updateAriaAttributes(items, opened, input) {
|
|
437
|
+
if (items === undefined || input === undefined) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (items.length === 0) {
|
|
442
|
+
input.removeAttribute('role');
|
|
443
|
+
input.removeAttribute('aria-expanded');
|
|
444
|
+
} else {
|
|
445
|
+
input.setAttribute('role', 'combobox');
|
|
446
|
+
input.setAttribute('aria-expanded', !!opened);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** @private */
|
|
451
|
+
__generateDropdownList(minSec, maxSec, step) {
|
|
452
|
+
if (step < 15 * 60 || !this.__validDayDivisor(step)) {
|
|
453
|
+
return [];
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const generatedList = [];
|
|
457
|
+
|
|
458
|
+
// Default step in overlay items is 1 hour
|
|
459
|
+
if (!step) {
|
|
460
|
+
step = 3600;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let time = -step + minSec;
|
|
464
|
+
while (time + step >= minSec && time + step <= maxSec) {
|
|
465
|
+
const timeObj = validateTime(this.__addStep(time * 1000, step), step);
|
|
466
|
+
time += step;
|
|
467
|
+
const formatted = this.i18n.formatTime(timeObj);
|
|
468
|
+
generatedList.push({ label: formatted, value: formatted });
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return generatedList;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Override an observer from `InputMixin`.
|
|
476
|
+
* @protected
|
|
477
|
+
* @override
|
|
478
|
+
*/
|
|
479
|
+
_valueChanged(value, oldValue) {
|
|
480
|
+
const parsedObj = (this.__memoValue = parseISOTime(value));
|
|
481
|
+
const newValue = formatISOTime(parsedObj) || '';
|
|
482
|
+
|
|
483
|
+
// Mark value set programmatically by the user
|
|
484
|
+
// as committed for the change event detection.
|
|
485
|
+
if (!this.__keepCommittedValue) {
|
|
486
|
+
this.__committedValue = value;
|
|
487
|
+
this.__committedUnparsableValue = '';
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (value !== '' && value !== null && !parsedObj) {
|
|
491
|
+
// Value can not be parsed, reset to the old one.
|
|
492
|
+
this.value = oldValue === undefined ? '' : oldValue;
|
|
493
|
+
} else if (value !== newValue) {
|
|
494
|
+
// Value can be parsed (e.g. 12 -> 12:00), adjust.
|
|
495
|
+
this.value = newValue;
|
|
496
|
+
} else if (this.__keepInvalidInput) {
|
|
497
|
+
// User input could not be parsed and was reset
|
|
498
|
+
// to empty string, do not update input value.
|
|
499
|
+
delete this.__keepInvalidInput;
|
|
500
|
+
} else {
|
|
501
|
+
this.__updateInputValue(parsedObj);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
this._toggleHasValue(this._hasValue);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** @private */
|
|
508
|
+
__comboBoxValueChanged(value, oldValue) {
|
|
509
|
+
if (value === '' && oldValue === undefined) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const parsedObj = this.__useMemo ? this.__memoValue : this.i18n.parseTime(value);
|
|
514
|
+
const newValue = this.i18n.formatTime(parsedObj) || '';
|
|
515
|
+
|
|
516
|
+
if (parsedObj) {
|
|
517
|
+
if (value !== newValue) {
|
|
518
|
+
this._comboBoxValue = newValue;
|
|
519
|
+
} else {
|
|
520
|
+
this.__keepCommittedValue = true;
|
|
521
|
+
this.__updateValue(parsedObj);
|
|
522
|
+
this.__keepCommittedValue = false;
|
|
523
|
+
}
|
|
524
|
+
} else {
|
|
525
|
+
// If the user input can not be parsed, set a flag
|
|
526
|
+
// that prevents `__valueChanged` from removing the input
|
|
527
|
+
// after setting the value property to an empty string below.
|
|
528
|
+
if (this.value !== '' && value !== '') {
|
|
529
|
+
this.__keepInvalidInput = true;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
this.__keepCommittedValue = true;
|
|
533
|
+
this.value = '';
|
|
534
|
+
this.__keepCommittedValue = false;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** @private */
|
|
539
|
+
__onComboBoxChange(event) {
|
|
540
|
+
event.stopPropagation();
|
|
541
|
+
this.__commitValueChange();
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Synchronizes the `_hasInputValue` property with the internal combo-box's one.
|
|
546
|
+
*
|
|
547
|
+
* @private
|
|
548
|
+
*/
|
|
549
|
+
__onComboBoxHasInputValueChanged() {
|
|
550
|
+
this._hasInputValue = this.$.comboBox._hasInputValue;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** @private */
|
|
554
|
+
__updateValue(obj) {
|
|
555
|
+
const timeString = formatISOTime(validateTime(obj, this.step)) || '';
|
|
556
|
+
this.value = timeString;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** @private */
|
|
560
|
+
__updateInputValue(obj) {
|
|
561
|
+
const timeString = this.i18n.formatTime(validateTime(obj, this.step)) || '';
|
|
562
|
+
this._comboBoxValue = timeString;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Returns true if `time` satisfies the `min` and `max` constraints (if any).
|
|
567
|
+
*
|
|
568
|
+
* @param {!TimePickerTime} time Value to check against constraints
|
|
569
|
+
* @return {boolean} True if `time` satisfies the constraints
|
|
570
|
+
* @protected
|
|
571
|
+
*/
|
|
572
|
+
_timeAllowed(time) {
|
|
573
|
+
const parsedMin = this.i18n.parseTime(this.min || MIN_ALLOWED_TIME);
|
|
574
|
+
const parsedMax = this.i18n.parseTime(this.max || MAX_ALLOWED_TIME);
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
(!this.__getMsec(parsedMin) || this.__getMsec(time) >= this.__getMsec(parsedMin)) &&
|
|
578
|
+
(!this.__getMsec(parsedMax) || this.__getMsec(time) <= this.__getMsec(parsedMax))
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Override method inherited from `InputControlMixin`.
|
|
584
|
+
* @protected
|
|
585
|
+
*/
|
|
586
|
+
_onClearButtonClick() {}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Override method inherited from `InputConstraintsMixin`.
|
|
590
|
+
* @protected
|
|
591
|
+
*/
|
|
592
|
+
_onChange() {}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Override method inherited from `InputMixin`.
|
|
596
|
+
* @protected
|
|
597
|
+
*/
|
|
598
|
+
_onInput() {}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Fired when the user commits a value change.
|
|
602
|
+
*
|
|
603
|
+
* @event change
|
|
604
|
+
*/
|
|
605
|
+
};
|