@vaadin/time-picker 24.6.0-alpha4 → 24.6.0-alpha5
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
|
@@ -9,15 +9,11 @@ import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
|
9
9
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
10
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
11
11
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
12
|
-
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
|
|
13
12
|
import { InputController } from '@vaadin/field-base/src/input-controller.js';
|
|
14
13
|
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';
|
|
15
|
-
import { PatternMixin } from '@vaadin/field-base/src/pattern-mixin.js';
|
|
16
14
|
import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
|
|
17
15
|
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
18
|
-
|
|
19
|
-
const MIN_ALLOWED_TIME = '00:00:00.000';
|
|
20
|
-
const MAX_ALLOWED_TIME = '23:59:59.999';
|
|
16
|
+
import { TimePickerMixin } from './vaadin-time-picker-mixin.js';
|
|
21
17
|
|
|
22
18
|
registerStyles('vaadin-time-picker', inputFieldShared, { moduleId: 'vaadin-time-picker-styles' });
|
|
23
19
|
|
|
@@ -99,10 +95,9 @@ registerStyles('vaadin-time-picker', inputFieldShared, { moduleId: 'vaadin-time-
|
|
|
99
95
|
* @extends HTMLElement
|
|
100
96
|
* @mixes ElementMixin
|
|
101
97
|
* @mixes ThemableMixin
|
|
102
|
-
* @mixes
|
|
103
|
-
* @mixes PatternMixin
|
|
98
|
+
* @mixes TimePickerMixin
|
|
104
99
|
*/
|
|
105
|
-
class TimePicker extends
|
|
100
|
+
class TimePicker extends TimePickerMixin(ThemableMixin(ElementMixin(PolymerElement))) {
|
|
106
101
|
static get is() {
|
|
107
102
|
return 'vaadin-time-picker';
|
|
108
103
|
}
|
|
@@ -174,221 +169,11 @@ class TimePicker extends PatternMixin(InputControlMixin(ThemableMixin(ElementMix
|
|
|
174
169
|
|
|
175
170
|
static get properties() {
|
|
176
171
|
return {
|
|
177
|
-
/**
|
|
178
|
-
* The time value for this element.
|
|
179
|
-
*
|
|
180
|
-
* Supported time formats are in ISO 8601:
|
|
181
|
-
* - `hh:mm` (default)
|
|
182
|
-
* - `hh:mm:ss`
|
|
183
|
-
* - `hh:mm:ss.fff`
|
|
184
|
-
* @type {string}
|
|
185
|
-
*/
|
|
186
|
-
value: {
|
|
187
|
-
type: String,
|
|
188
|
-
notify: true,
|
|
189
|
-
value: '',
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* True if the dropdown is open, false otherwise.
|
|
194
|
-
*/
|
|
195
|
-
opened: {
|
|
196
|
-
type: Boolean,
|
|
197
|
-
notify: true,
|
|
198
|
-
value: false,
|
|
199
|
-
reflectToAttribute: true,
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Minimum time allowed.
|
|
204
|
-
*
|
|
205
|
-
* Supported time formats are in ISO 8601:
|
|
206
|
-
* - `hh:mm`
|
|
207
|
-
* - `hh:mm:ss`
|
|
208
|
-
* - `hh:mm:ss.fff`
|
|
209
|
-
* @type {string}
|
|
210
|
-
*/
|
|
211
|
-
min: {
|
|
212
|
-
type: String,
|
|
213
|
-
value: '',
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Maximum time allowed.
|
|
218
|
-
*
|
|
219
|
-
* Supported time formats are in ISO 8601:
|
|
220
|
-
* - `hh:mm`
|
|
221
|
-
* - `hh:mm:ss`
|
|
222
|
-
* - `hh:mm:ss.fff`
|
|
223
|
-
* @type {string}
|
|
224
|
-
*/
|
|
225
|
-
max: {
|
|
226
|
-
type: String,
|
|
227
|
-
value: '',
|
|
228
|
-
},
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Defines the time interval (in seconds) between the items displayed
|
|
232
|
-
* in the time selection box. The default is 1 hour (i.e. `3600`).
|
|
233
|
-
*
|
|
234
|
-
* It also configures the precision of the value string. By default
|
|
235
|
-
* the component formats values as `hh:mm` but setting a step value
|
|
236
|
-
* lower than one minute or one second, format resolution changes to
|
|
237
|
-
* `hh:mm:ss` and `hh:mm:ss.fff` respectively.
|
|
238
|
-
*
|
|
239
|
-
* Unit must be set in seconds, and for correctly configuring intervals
|
|
240
|
-
* in the dropdown, it need to evenly divide a day.
|
|
241
|
-
*
|
|
242
|
-
* Note: it is possible to define step that is dividing an hour in inexact
|
|
243
|
-
* fragments (i.e. 5760 seconds which equals 1 hour 36 minutes), but it is
|
|
244
|
-
* not recommended to use it for better UX experience.
|
|
245
|
-
*/
|
|
246
|
-
step: {
|
|
247
|
-
type: Number,
|
|
248
|
-
},
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Set true to prevent the overlay from opening automatically.
|
|
252
|
-
* @attr {boolean} auto-open-disabled
|
|
253
|
-
*/
|
|
254
|
-
autoOpenDisabled: Boolean,
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* A space-delimited list of CSS class names to set on the overlay element.
|
|
258
|
-
*
|
|
259
|
-
* @attr {string} overlay-class
|
|
260
|
-
*/
|
|
261
|
-
overlayClass: {
|
|
262
|
-
type: String,
|
|
263
|
-
},
|
|
264
|
-
|
|
265
|
-
/** @private */
|
|
266
|
-
__dropdownItems: {
|
|
267
|
-
type: Array,
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* The object used to localize this component.
|
|
272
|
-
* To change the default localization, replace the entire
|
|
273
|
-
* _i18n_ object or just the property you want to modify.
|
|
274
|
-
*
|
|
275
|
-
* The object has the following JSON structure:
|
|
276
|
-
*
|
|
277
|
-
* ```
|
|
278
|
-
* {
|
|
279
|
-
* // A function to format given `Object` as
|
|
280
|
-
* // time string. Object is in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
281
|
-
* formatTime: (time) => {
|
|
282
|
-
* // returns a string representation of the given
|
|
283
|
-
* // object in `hh` / 'hh:mm' / 'hh:mm:ss' / 'hh:mm:ss.fff' - formats
|
|
284
|
-
* },
|
|
285
|
-
*
|
|
286
|
-
* // A function to parse the given text to an `Object` in the format
|
|
287
|
-
* // `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`.
|
|
288
|
-
* // Must properly parse (at least) text
|
|
289
|
-
* // formatted by `formatTime`.
|
|
290
|
-
* parseTime: text => {
|
|
291
|
-
* // Parses a string in object/string that can be formatted by`formatTime`.
|
|
292
|
-
* }
|
|
293
|
-
* }
|
|
294
|
-
* ```
|
|
295
|
-
*
|
|
296
|
-
* Both `formatTime` and `parseTime` need to be implemented
|
|
297
|
-
* to ensure the component works properly.
|
|
298
|
-
*
|
|
299
|
-
* @type {!TimePickerI18n}
|
|
300
|
-
*/
|
|
301
|
-
i18n: {
|
|
302
|
-
type: Object,
|
|
303
|
-
value: () => {
|
|
304
|
-
return {
|
|
305
|
-
formatTime: (time) => {
|
|
306
|
-
if (!time) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const pad = (num = 0, fmt = '00') => (fmt + num).substr((fmt + num).length - fmt.length);
|
|
311
|
-
// Always display hour and minute
|
|
312
|
-
let timeString = `${pad(time.hours)}:${pad(time.minutes)}`;
|
|
313
|
-
// Adding second and millisecond depends on resolution
|
|
314
|
-
if (time.seconds !== undefined) {
|
|
315
|
-
timeString += `:${pad(time.seconds)}`;
|
|
316
|
-
}
|
|
317
|
-
if (time.milliseconds !== undefined) {
|
|
318
|
-
timeString += `.${pad(time.milliseconds, '000')}`;
|
|
319
|
-
}
|
|
320
|
-
return timeString;
|
|
321
|
-
},
|
|
322
|
-
parseTime: (text) => {
|
|
323
|
-
// Parsing with RegExp to ensure correct format
|
|
324
|
-
const MATCH_HOURS = '(\\d|[0-1]\\d|2[0-3])';
|
|
325
|
-
const MATCH_MINUTES = '(\\d|[0-5]\\d)';
|
|
326
|
-
const MATCH_SECONDS = MATCH_MINUTES;
|
|
327
|
-
const MATCH_MILLISECONDS = '(\\d{1,3})';
|
|
328
|
-
const re = new RegExp(
|
|
329
|
-
`^${MATCH_HOURS}(?::${MATCH_MINUTES}(?::${MATCH_SECONDS}(?:\\.${MATCH_MILLISECONDS})?)?)?$`,
|
|
330
|
-
'u',
|
|
331
|
-
);
|
|
332
|
-
const parts = re.exec(text);
|
|
333
|
-
if (parts) {
|
|
334
|
-
// Allows setting the milliseconds with hundreds and tens precision
|
|
335
|
-
if (parts[4]) {
|
|
336
|
-
while (parts[4].length < 3) {
|
|
337
|
-
parts[4] += '0';
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return { hours: parts[1], minutes: parts[2], seconds: parts[3], milliseconds: parts[4] };
|
|
341
|
-
}
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
|
-
},
|
|
345
|
-
},
|
|
346
|
-
|
|
347
|
-
/** @private */
|
|
348
|
-
_comboBoxValue: {
|
|
349
|
-
type: String,
|
|
350
|
-
observer: '__comboBoxValueChanged',
|
|
351
|
-
},
|
|
352
|
-
|
|
353
172
|
/** @private */
|
|
354
173
|
_inputContainer: Object,
|
|
355
174
|
};
|
|
356
175
|
}
|
|
357
176
|
|
|
358
|
-
static get observers() {
|
|
359
|
-
return [
|
|
360
|
-
'__updateAriaAttributes(__dropdownItems, opened, inputElement)',
|
|
361
|
-
'__updateDropdownItems(i18n.*, min, max, step)',
|
|
362
|
-
];
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
static get constraints() {
|
|
366
|
-
return [...super.constraints, 'min', 'max'];
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Used by `InputControlMixin` as a reference to the clear button element.
|
|
371
|
-
* @protected
|
|
372
|
-
* @return {!HTMLElement}
|
|
373
|
-
*/
|
|
374
|
-
get clearElement() {
|
|
375
|
-
return this.$.clearButton;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* The input element's value when it cannot be parsed as a time, and an empty string otherwise.
|
|
380
|
-
*
|
|
381
|
-
* @private
|
|
382
|
-
* @return {string}
|
|
383
|
-
*/
|
|
384
|
-
get __unparsableValue() {
|
|
385
|
-
if (this._inputElementValue && !this.i18n.parseTime(this._inputElementValue)) {
|
|
386
|
-
return this._inputElementValue;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return '';
|
|
390
|
-
}
|
|
391
|
-
|
|
392
177
|
/** @protected */
|
|
393
178
|
ready() {
|
|
394
179
|
super.ready();
|
|
@@ -419,443 +204,6 @@ class TimePicker extends PatternMixin(InputControlMixin(ThemableMixin(ElementMix
|
|
|
419
204
|
this._tooltipController.setAriaTarget(this.inputElement);
|
|
420
205
|
this.addController(this._tooltipController);
|
|
421
206
|
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Override method inherited from `InputMixin` to forward the input to combo-box.
|
|
425
|
-
* @protected
|
|
426
|
-
* @override
|
|
427
|
-
*/
|
|
428
|
-
_inputElementChanged(input) {
|
|
429
|
-
super._inputElementChanged(input);
|
|
430
|
-
|
|
431
|
-
if (input) {
|
|
432
|
-
this.$.comboBox._setInputElement(input);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Opens the dropdown list.
|
|
438
|
-
*/
|
|
439
|
-
open() {
|
|
440
|
-
if (!this.disabled && !this.readonly) {
|
|
441
|
-
this.opened = true;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Closes the dropdown list.
|
|
447
|
-
*/
|
|
448
|
-
close() {
|
|
449
|
-
this.opened = false;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Returns true if the current input value satisfies all constraints (if any).
|
|
454
|
-
* You can override this method for custom validations.
|
|
455
|
-
*
|
|
456
|
-
* @return {boolean} True if the value is valid
|
|
457
|
-
*/
|
|
458
|
-
checkValidity() {
|
|
459
|
-
return !!(
|
|
460
|
-
this.inputElement.checkValidity() &&
|
|
461
|
-
(!this.value || this._timeAllowed(this.i18n.parseTime(this.value))) &&
|
|
462
|
-
(!this._comboBoxValue || this.i18n.parseTime(this._comboBoxValue))
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* @param {boolean} focused
|
|
468
|
-
* @override
|
|
469
|
-
* @protected
|
|
470
|
-
*/
|
|
471
|
-
_setFocused(focused) {
|
|
472
|
-
super._setFocused(focused);
|
|
473
|
-
|
|
474
|
-
if (!focused) {
|
|
475
|
-
// Do not validate when focusout is caused by document
|
|
476
|
-
// losing focus, which happens on browser tab switch.
|
|
477
|
-
if (document.hasFocus()) {
|
|
478
|
-
this.validate();
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/** @private */
|
|
484
|
-
__validDayDivisor(step) {
|
|
485
|
-
// Valid if undefined, or exact divides a day, or has millisecond resolution
|
|
486
|
-
return !step || (24 * 3600) % step === 0 || (step < 1 && ((step % 1) * 1000) % 1 === 0);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Override an event listener from `KeyboardMixin`.
|
|
491
|
-
* @param {!KeyboardEvent} e
|
|
492
|
-
* @protected
|
|
493
|
-
*/
|
|
494
|
-
_onKeyDown(e) {
|
|
495
|
-
super._onKeyDown(e);
|
|
496
|
-
|
|
497
|
-
if (this.readonly || this.disabled || this.__dropdownItems.length) {
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const stepResolution = (this.__validDayDivisor(this.step) && this.step) || 60;
|
|
502
|
-
|
|
503
|
-
if (e.keyCode === 40) {
|
|
504
|
-
this.__onArrowPressWithStep(-stepResolution);
|
|
505
|
-
} else if (e.keyCode === 38) {
|
|
506
|
-
this.__onArrowPressWithStep(stepResolution);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Override an event listener from `KeyboardMixin`.
|
|
512
|
-
* Do not call `super` in order to override clear
|
|
513
|
-
* button logic defined in `InputControlMixin`.
|
|
514
|
-
* @param {Event} event
|
|
515
|
-
* @protected
|
|
516
|
-
*/
|
|
517
|
-
_onEscape() {
|
|
518
|
-
// Do nothing, the internal combo-box handles Escape.
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/** @private */
|
|
522
|
-
__onArrowPressWithStep(step) {
|
|
523
|
-
const objWithStep = this.__addStep(this.__getMsec(this.__memoValue), step, true);
|
|
524
|
-
this.__memoValue = objWithStep;
|
|
525
|
-
|
|
526
|
-
// Setting `_comboBoxValue` property triggers the synchronous
|
|
527
|
-
// observer where the value can be parsed again, so we set
|
|
528
|
-
// this flag to ensure it does not alter the value.
|
|
529
|
-
this.__useMemo = true;
|
|
530
|
-
this._comboBoxValue = this.i18n.formatTime(objWithStep);
|
|
531
|
-
this.__useMemo = false;
|
|
532
|
-
|
|
533
|
-
this.__commitValueChange();
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Depending on the nature of the value change that has occurred since
|
|
538
|
-
* the last commit attempt, triggers validation and fires an event:
|
|
539
|
-
*
|
|
540
|
-
* Value change | Event
|
|
541
|
-
* -------------------------|-------------------
|
|
542
|
-
* empty => parsable | change
|
|
543
|
-
* empty => unparsable | unparsable-change
|
|
544
|
-
* parsable => empty | change
|
|
545
|
-
* parsable => parsable | change
|
|
546
|
-
* parsable => unparsable | change
|
|
547
|
-
* unparsable => empty | unparsable-change
|
|
548
|
-
* unparsable => parsable | change
|
|
549
|
-
* unparsable => unparsable | unparsable-change
|
|
550
|
-
*
|
|
551
|
-
* @private
|
|
552
|
-
*/
|
|
553
|
-
__commitValueChange() {
|
|
554
|
-
const unparsableValue = this.__unparsableValue;
|
|
555
|
-
|
|
556
|
-
if (this.__committedValue !== this.value) {
|
|
557
|
-
this.validate();
|
|
558
|
-
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
|
559
|
-
} else if (this.__committedUnparsableValue !== unparsableValue) {
|
|
560
|
-
this.validate();
|
|
561
|
-
this.dispatchEvent(new CustomEvent('unparsable-change'));
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
this.__committedValue = this.value;
|
|
565
|
-
this.__committedUnparsableValue = unparsableValue;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Returning milliseconds from Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
570
|
-
* @private
|
|
571
|
-
*/
|
|
572
|
-
__getMsec(obj) {
|
|
573
|
-
let result = ((obj && obj.hours) || 0) * 60 * 60 * 1000;
|
|
574
|
-
result += ((obj && obj.minutes) || 0) * 60 * 1000;
|
|
575
|
-
result += ((obj && obj.seconds) || 0) * 1000;
|
|
576
|
-
result += (obj && parseInt(obj.milliseconds)) || 0;
|
|
577
|
-
|
|
578
|
-
return result;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Returning seconds from Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
583
|
-
* @private
|
|
584
|
-
*/
|
|
585
|
-
__getSec(obj) {
|
|
586
|
-
let result = ((obj && obj.hours) || 0) * 60 * 60;
|
|
587
|
-
result += ((obj && obj.minutes) || 0) * 60;
|
|
588
|
-
result += (obj && obj.seconds) || 0;
|
|
589
|
-
result += (obj && obj.milliseconds / 1000) || 0;
|
|
590
|
-
|
|
591
|
-
return result;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* Returning Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
|
|
596
|
-
* from the result of adding step value in milliseconds to the milliseconds amount.
|
|
597
|
-
* With `precision` parameter rounding the value to the closest step valid interval.
|
|
598
|
-
* @private
|
|
599
|
-
*/
|
|
600
|
-
__addStep(msec, step, precision) {
|
|
601
|
-
// If the time is `00:00` and step changes value downwards, it should be considered as `24:00`
|
|
602
|
-
if (msec === 0 && step < 0) {
|
|
603
|
-
msec = 24 * 60 * 60 * 1000;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const stepMsec = step * 1000;
|
|
607
|
-
const diffToNext = msec % stepMsec;
|
|
608
|
-
if (stepMsec < 0 && diffToNext && precision) {
|
|
609
|
-
msec -= diffToNext;
|
|
610
|
-
} else if (stepMsec > 0 && diffToNext && precision) {
|
|
611
|
-
msec -= diffToNext - stepMsec;
|
|
612
|
-
} else {
|
|
613
|
-
msec += stepMsec;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const hh = Math.floor(msec / 1000 / 60 / 60);
|
|
617
|
-
msec -= hh * 1000 * 60 * 60;
|
|
618
|
-
const mm = Math.floor(msec / 1000 / 60);
|
|
619
|
-
msec -= mm * 1000 * 60;
|
|
620
|
-
const ss = Math.floor(msec / 1000);
|
|
621
|
-
msec -= ss * 1000;
|
|
622
|
-
|
|
623
|
-
return { hours: hh < 24 ? hh : 0, minutes: mm, seconds: ss, milliseconds: msec };
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/** @private */
|
|
627
|
-
__updateDropdownItems(_i18n, min, max, step) {
|
|
628
|
-
const minTimeObj = this.__validateTime(this.__parseISO(min || MIN_ALLOWED_TIME));
|
|
629
|
-
const minSec = this.__getSec(minTimeObj);
|
|
630
|
-
|
|
631
|
-
const maxTimeObj = this.__validateTime(this.__parseISO(max || MAX_ALLOWED_TIME));
|
|
632
|
-
const maxSec = this.__getSec(maxTimeObj);
|
|
633
|
-
|
|
634
|
-
this.__dropdownItems = this.__generateDropdownList(minSec, maxSec, step);
|
|
635
|
-
|
|
636
|
-
if (step !== this.__oldStep) {
|
|
637
|
-
this.__oldStep = step;
|
|
638
|
-
const parsedObj = this.__validateTime(this.__parseISO(this.value));
|
|
639
|
-
this.__updateValue(parsedObj);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (this.value) {
|
|
643
|
-
this._comboBoxValue = this.i18n.formatTime(this.i18n.parseTime(this.value));
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/** @private */
|
|
648
|
-
__updateAriaAttributes(items, opened, input) {
|
|
649
|
-
if (items === undefined || input === undefined) {
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
if (items.length === 0) {
|
|
654
|
-
input.removeAttribute('role');
|
|
655
|
-
input.removeAttribute('aria-expanded');
|
|
656
|
-
} else {
|
|
657
|
-
input.setAttribute('role', 'combobox');
|
|
658
|
-
input.setAttribute('aria-expanded', !!opened);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/** @private */
|
|
663
|
-
__generateDropdownList(minSec, maxSec, step) {
|
|
664
|
-
if (step < 15 * 60 || !this.__validDayDivisor(step)) {
|
|
665
|
-
return [];
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
const generatedList = [];
|
|
669
|
-
|
|
670
|
-
// Default step in overlay items is 1 hour
|
|
671
|
-
if (!step) {
|
|
672
|
-
step = 3600;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
let time = -step + minSec;
|
|
676
|
-
while (time + step >= minSec && time + step <= maxSec) {
|
|
677
|
-
const timeObj = this.__validateTime(this.__addStep(time * 1000, step));
|
|
678
|
-
time += step;
|
|
679
|
-
const formatted = this.i18n.formatTime(timeObj);
|
|
680
|
-
generatedList.push({ label: formatted, value: formatted });
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
return generatedList;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* Override an observer from `InputMixin`.
|
|
688
|
-
* @protected
|
|
689
|
-
* @override
|
|
690
|
-
*/
|
|
691
|
-
_valueChanged(value, oldValue) {
|
|
692
|
-
const parsedObj = (this.__memoValue = this.__parseISO(value));
|
|
693
|
-
const newValue = this.__formatISO(parsedObj) || '';
|
|
694
|
-
|
|
695
|
-
// Mark value set programmatically by the user
|
|
696
|
-
// as committed for the change event detection.
|
|
697
|
-
if (!this.__keepCommittedValue) {
|
|
698
|
-
this.__committedValue = value;
|
|
699
|
-
this.__committedUnparsableValue = '';
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
if (value !== '' && value !== null && !parsedObj) {
|
|
703
|
-
// Value can not be parsed, reset to the old one.
|
|
704
|
-
this.value = oldValue === undefined ? '' : oldValue;
|
|
705
|
-
} else if (value !== newValue) {
|
|
706
|
-
// Value can be parsed (e.g. 12 -> 12:00), adjust.
|
|
707
|
-
this.value = newValue;
|
|
708
|
-
} else if (this.__keepInvalidInput) {
|
|
709
|
-
// User input could not be parsed and was reset
|
|
710
|
-
// to empty string, do not update input value.
|
|
711
|
-
delete this.__keepInvalidInput;
|
|
712
|
-
} else {
|
|
713
|
-
this.__updateInputValue(parsedObj);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
this._toggleHasValue(this._hasValue);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/** @private */
|
|
720
|
-
__comboBoxValueChanged(value, oldValue) {
|
|
721
|
-
if (value === '' && oldValue === undefined) {
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
const parsedObj = this.__useMemo ? this.__memoValue : this.i18n.parseTime(value);
|
|
726
|
-
const newValue = this.i18n.formatTime(parsedObj) || '';
|
|
727
|
-
|
|
728
|
-
if (parsedObj) {
|
|
729
|
-
if (value !== newValue) {
|
|
730
|
-
this._comboBoxValue = newValue;
|
|
731
|
-
} else {
|
|
732
|
-
this.__keepCommittedValue = true;
|
|
733
|
-
this.__updateValue(parsedObj);
|
|
734
|
-
this.__keepCommittedValue = false;
|
|
735
|
-
}
|
|
736
|
-
} else {
|
|
737
|
-
// If the user input can not be parsed, set a flag
|
|
738
|
-
// that prevents `__valueChanged` from removing the input
|
|
739
|
-
// after setting the value property to an empty string below.
|
|
740
|
-
if (this.value !== '' && value !== '') {
|
|
741
|
-
this.__keepInvalidInput = true;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
this.__keepCommittedValue = true;
|
|
745
|
-
this.value = '';
|
|
746
|
-
this.__keepCommittedValue = false;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/** @private */
|
|
751
|
-
__onComboBoxChange(event) {
|
|
752
|
-
event.stopPropagation();
|
|
753
|
-
this.__commitValueChange();
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Synchronizes the `_hasInputValue` property with the internal combo-box's one.
|
|
758
|
-
*
|
|
759
|
-
* @private
|
|
760
|
-
*/
|
|
761
|
-
__onComboBoxHasInputValueChanged() {
|
|
762
|
-
this._hasInputValue = this.$.comboBox._hasInputValue;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
/** @private */
|
|
766
|
-
__updateValue(obj) {
|
|
767
|
-
const timeString = this.__formatISO(this.__validateTime(obj)) || '';
|
|
768
|
-
this.value = timeString;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
/** @private */
|
|
772
|
-
__updateInputValue(obj) {
|
|
773
|
-
const timeString = this.i18n.formatTime(this.__validateTime(obj)) || '';
|
|
774
|
-
this._comboBoxValue = timeString;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/** @private */
|
|
778
|
-
__validateTime(timeObject) {
|
|
779
|
-
if (timeObject) {
|
|
780
|
-
const stepSegment = this.__getStepSegment();
|
|
781
|
-
timeObject.hours = parseInt(timeObject.hours);
|
|
782
|
-
timeObject.minutes = parseInt(timeObject.minutes || 0);
|
|
783
|
-
timeObject.seconds = stepSegment < 3 ? undefined : parseInt(timeObject.seconds || 0);
|
|
784
|
-
timeObject.milliseconds = stepSegment < 4 ? undefined : parseInt(timeObject.milliseconds || 0);
|
|
785
|
-
}
|
|
786
|
-
return timeObject;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
/** @private */
|
|
790
|
-
__getStepSegment() {
|
|
791
|
-
if (this.step % 3600 === 0) {
|
|
792
|
-
// Accept hours
|
|
793
|
-
return 1;
|
|
794
|
-
} else if (this.step % 60 === 0 || !this.step) {
|
|
795
|
-
// Accept minutes
|
|
796
|
-
return 2;
|
|
797
|
-
} else if (this.step % 1 === 0) {
|
|
798
|
-
// Accept seconds
|
|
799
|
-
return 3;
|
|
800
|
-
} else if (this.step < 1) {
|
|
801
|
-
// Accept milliseconds
|
|
802
|
-
return 4;
|
|
803
|
-
}
|
|
804
|
-
return undefined;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
/** @private */
|
|
808
|
-
__formatISO(time) {
|
|
809
|
-
// The default i18n formatter implementation is ISO 8601 compliant
|
|
810
|
-
return TimePicker.properties.i18n.value().formatTime(time);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
/** @private */
|
|
814
|
-
__parseISO(text) {
|
|
815
|
-
// The default i18n parser implementation is ISO 8601 compliant
|
|
816
|
-
return TimePicker.properties.i18n.value().parseTime(text);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
/**
|
|
820
|
-
* Returns true if `time` satisfies the `min` and `max` constraints (if any).
|
|
821
|
-
*
|
|
822
|
-
* @param {!TimePickerTime} time Value to check against constraints
|
|
823
|
-
* @return {boolean} True if `time` satisfies the constraints
|
|
824
|
-
* @protected
|
|
825
|
-
*/
|
|
826
|
-
_timeAllowed(time) {
|
|
827
|
-
const parsedMin = this.i18n.parseTime(this.min || MIN_ALLOWED_TIME);
|
|
828
|
-
const parsedMax = this.i18n.parseTime(this.max || MAX_ALLOWED_TIME);
|
|
829
|
-
|
|
830
|
-
return (
|
|
831
|
-
(!this.__getMsec(parsedMin) || this.__getMsec(time) >= this.__getMsec(parsedMin)) &&
|
|
832
|
-
(!this.__getMsec(parsedMax) || this.__getMsec(time) <= this.__getMsec(parsedMax))
|
|
833
|
-
);
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
/**
|
|
837
|
-
* Override method inherited from `InputControlMixin`.
|
|
838
|
-
* @protected
|
|
839
|
-
*/
|
|
840
|
-
_onClearButtonClick() {}
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Override method inherited from `InputConstraintsMixin`.
|
|
844
|
-
* @protected
|
|
845
|
-
*/
|
|
846
|
-
_onChange() {}
|
|
847
|
-
|
|
848
|
-
/**
|
|
849
|
-
* Override method inherited from `InputMixin`.
|
|
850
|
-
* @protected
|
|
851
|
-
*/
|
|
852
|
-
_onInput() {}
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* Fired when the user commits a value change.
|
|
856
|
-
*
|
|
857
|
-
* @event change
|
|
858
|
-
*/
|
|
859
207
|
}
|
|
860
208
|
|
|
861
209
|
defineCustomElement(TimePicker);
|