@vaadin/time-picker 22.0.0-alpha7

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.
@@ -0,0 +1,684 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
7
+ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
8
+ import { AriaLabelController } from '@vaadin/field-base/src/aria-label-controller.js';
9
+ import { InputController } from '@vaadin/field-base/src/input-controller.js';
10
+ import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
11
+ import { PatternMixin } from '@vaadin/field-base/src/pattern-mixin.js';
12
+ import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
13
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
14
+ import { registerStyles } from '@vaadin/vaadin-themable-mixin/register-styles.js';
15
+ import '@vaadin/input-container/src/vaadin-input-container.js';
16
+ import './vaadin-time-picker-combo-box.js';
17
+
18
+ registerStyles('vaadin-time-picker', inputFieldShared, { moduleId: 'vaadin-time-picker-styles' });
19
+
20
+ /**
21
+ * `<vaadin-time-picker>` is a Web Component providing a time-selection field.
22
+ *
23
+ * ```html
24
+ * <vaadin-time-picker></vaadin-time-picker>
25
+ * ```
26
+ * ```js
27
+ * timePicker.value = '14:30';
28
+ * ```
29
+ *
30
+ * When the selected `value` is changed, a `value-changed` event is triggered.
31
+ *
32
+ * ### Styling
33
+ *
34
+ * The following custom properties are available for styling:
35
+ *
36
+ * Custom property | Description | Default
37
+ * ------------------------------------------|----------------------------|---------
38
+ * `--vaadin-field-default-width` | Default width of the field | `12em`
39
+ * `--vaadin-time-picker-overlay-max-height` | Max height of the overlay | `65vh`
40
+ *
41
+ * The following shadow DOM parts are available for styling:
42
+ *
43
+ * Part name | Description
44
+ * ----------------|----------------
45
+ * `clear-button` | The clear button
46
+ * `input-field` | Input element wrapper
47
+ * `toggle-button` | The toggle button
48
+ * `label` | The label element
49
+ * `error-message` | The error message element
50
+ * `helper-text` | The helper text element wrapper
51
+ *
52
+ * See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
53
+ *
54
+ * The following state attributes are available for styling:
55
+ *
56
+ * Attribute | Description | Part name
57
+ * -------------|------------------------------------------|------------
58
+ * `disabled` | Set to a disabled time picker | :host
59
+ * `readonly` | Set to a read only time picker | :host
60
+ * `invalid` | Set when the element is invalid | :host
61
+ * `focused` | Set when the element is focused | :host
62
+ * `focus-ring` | Set when the element is keyboard focused | :host
63
+ *
64
+ * ### Internal components
65
+ *
66
+ * In addition to `<vaadin-time-picker>` itself, the following internal
67
+ * components are themable:
68
+ *
69
+ * - `<vaadin-time-picker-combo-box>` - has the same API as [`<vaadin-combo-box-light>`](#/elements/vaadin-combo-box-light).
70
+ *
71
+ * Note: the `theme` attribute value set on `<vaadin-time-picker>` is
72
+ * propagated to the internal components listed above.
73
+ *
74
+ * @fires {Event} change - Fired when the user commits a value change.
75
+ * @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
76
+ * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
77
+ *
78
+ * @extends HTMLElement
79
+ * @mixes ElementMixin
80
+ * @mixes ThemableMixin
81
+ * @mixes InputControlMixin
82
+ * @mixes PatternMixin
83
+ */
84
+ class TimePicker extends PatternMixin(InputControlMixin(ThemableMixin(ElementMixin(PolymerElement)))) {
85
+ static get is() {
86
+ return 'vaadin-time-picker';
87
+ }
88
+
89
+ static get template() {
90
+ return html`
91
+ <style>
92
+ /* See https://github.com/vaadin/vaadin-time-picker/issues/145 */
93
+ :host([dir='rtl']) [part='input-field'] {
94
+ direction: ltr;
95
+ }
96
+
97
+ :host([dir='rtl']) [part='input-field'] ::slotted(input)::placeholder {
98
+ direction: rtl;
99
+ text-align: left;
100
+ }
101
+
102
+ [part~='toggle-button'] {
103
+ cursor: pointer;
104
+ }
105
+ </style>
106
+
107
+ <div class="vaadin-time-picker-container">
108
+ <div part="label" on-click="focus">
109
+ <slot name="label"></slot>
110
+ <span part="required-indicator" aria-hidden="true"></span>
111
+ </div>
112
+
113
+ <vaadin-time-picker-combo-box
114
+ id="comboBox"
115
+ filtered-items="[[__dropdownItems]]"
116
+ value="{{_comboBoxValue}}"
117
+ disabled="[[disabled]]"
118
+ readonly="[[readonly]]"
119
+ auto-open-disabled="[[autoOpenDisabled]]"
120
+ position-target="[[_inputContainer]]"
121
+ theme$="[[theme]]"
122
+ on-change="__onChange"
123
+ >
124
+ <vaadin-input-container
125
+ part="input-field"
126
+ readonly="[[readonly]]"
127
+ disabled="[[disabled]]"
128
+ invalid="[[invalid]]"
129
+ theme$="[[theme]]"
130
+ >
131
+ <slot name="prefix" slot="prefix"></slot>
132
+ <slot name="input"></slot>
133
+ <div id="clearButton" part="clear-button" slot="suffix"></div>
134
+ <div id="toggleButton" class="toggle-button" part="toggle-button" slot="suffix"></div>
135
+ </vaadin-input-container>
136
+ </vaadin-time-picker-combo-box>
137
+
138
+ <div part="helper-text">
139
+ <slot name="helper"></slot>
140
+ </div>
141
+
142
+ <div part="error-message">
143
+ <slot name="error-message"></slot>
144
+ </div>
145
+ </div>
146
+ `;
147
+ }
148
+
149
+ static get properties() {
150
+ return {
151
+ /**
152
+ * The time value for this element.
153
+ *
154
+ * Supported time formats are in ISO 8601:
155
+ * - `hh:mm` (default)
156
+ * - `hh:mm:ss`
157
+ * - `hh:mm:ss.fff`
158
+ * @type {string}
159
+ */
160
+ value: {
161
+ type: String,
162
+ notify: true,
163
+ value: ''
164
+ },
165
+
166
+ /**
167
+ * Minimum time allowed.
168
+ *
169
+ * Supported time formats are in ISO 8601:
170
+ * - `hh:mm`
171
+ * - `hh:mm:ss`
172
+ * - `hh:mm:ss.fff`
173
+ * @type {string}
174
+ */
175
+ min: {
176
+ type: String,
177
+ value: '00:00:00.000'
178
+ },
179
+
180
+ /**
181
+ * Maximum time allowed.
182
+ *
183
+ * Supported time formats are in ISO 8601:
184
+ * - `hh:mm`
185
+ * - `hh:mm:ss`
186
+ * - `hh:mm:ss.fff`
187
+ * @type {string}
188
+ */
189
+ max: {
190
+ type: String,
191
+ value: '23:59:59.999'
192
+ },
193
+
194
+ /**
195
+ * Defines the time interval (in seconds) between the items displayed
196
+ * in the time selection box. The default is 1 hour (i.e. `3600`).
197
+ *
198
+ * It also configures the precision of the value string. By default
199
+ * the component formats values as `hh:mm` but setting a step value
200
+ * lower than one minute or one second, format resolution changes to
201
+ * `hh:mm:ss` and `hh:mm:ss.fff` respectively.
202
+ *
203
+ * Unit must be set in seconds, and for correctly configuring intervals
204
+ * in the dropdown, it need to evenly divide a day.
205
+ *
206
+ * Note: it is possible to define step that is dividing an hour in inexact
207
+ * fragments (i.e. 5760 seconds which equals 1 hour 36 minutes), but it is
208
+ * not recommended to use it for better UX experience.
209
+ */
210
+ step: {
211
+ type: Number
212
+ },
213
+
214
+ /**
215
+ * Set true to prevent the overlay from opening automatically.
216
+ * @attr {boolean} auto-open-disabled
217
+ */
218
+ autoOpenDisabled: Boolean,
219
+
220
+ /** @private */
221
+ __dropdownItems: {
222
+ type: Array
223
+ },
224
+
225
+ /**
226
+ * The object used to localize this component.
227
+ * To change the default localization, replace the entire
228
+ * _i18n_ object or just the property you want to modify.
229
+ *
230
+ * The object has the following JSON structure:
231
+ *
232
+ * ```
233
+ * {
234
+ * // A function to format given `Object` as
235
+ * // time string. Object is in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
236
+ * formatTime: (time) => {
237
+ * // returns a string representation of the given
238
+ * // object in `hh` / 'hh:mm' / 'hh:mm:ss' / 'hh:mm:ss.fff' - formats
239
+ * },
240
+ *
241
+ * // A function to parse the given text to an `Object` in the format
242
+ * // `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`.
243
+ * // Must properly parse (at least) text
244
+ * // formatted by `formatTime`.
245
+ * parseTime: text => {
246
+ * // Parses a string in object/string that can be formatted by`formatTime`.
247
+ * }
248
+ * }
249
+ * ```
250
+ *
251
+ * Both `formatTime` and `parseTime` need to be implemented
252
+ * to ensure the component works properly.
253
+ *
254
+ * @type {!TimePickerI18n}
255
+ */
256
+ i18n: {
257
+ type: Object,
258
+ value: () => {
259
+ return {
260
+ formatTime: (time) => {
261
+ if (!time) {
262
+ return;
263
+ }
264
+
265
+ const pad = (num = 0, fmt = '00') => (fmt + num).substr((fmt + num).length - fmt.length);
266
+ // Always display hour and minute
267
+ let timeString = `${pad(time.hours)}:${pad(time.minutes)}`;
268
+ // Adding second and millisecond depends on resolution
269
+ time.seconds !== undefined && (timeString += `:${pad(time.seconds)}`);
270
+ time.milliseconds !== undefined && (timeString += `.${pad(time.milliseconds, '000')}`);
271
+ return timeString;
272
+ },
273
+ parseTime: (text) => {
274
+ // Parsing with RegExp to ensure correct format
275
+ const MATCH_HOURS = '(\\d|[0-1]\\d|2[0-3])';
276
+ const MATCH_MINUTES = '(\\d|[0-5]\\d)';
277
+ const MATCH_SECONDS = MATCH_MINUTES;
278
+ const MATCH_MILLISECONDS = '(\\d{1,3})';
279
+ const re = new RegExp(
280
+ `^${MATCH_HOURS}(?::${MATCH_MINUTES}(?::${MATCH_SECONDS}(?:\\.${MATCH_MILLISECONDS})?)?)?$`
281
+ );
282
+ const parts = re.exec(text);
283
+ if (parts) {
284
+ // Allows setting the milliseconds with hundreds and tens precision
285
+ if (parts[4]) {
286
+ while (parts[4].length < 3) {
287
+ parts[4] += '0';
288
+ }
289
+ }
290
+ return { hours: parts[1], minutes: parts[2], seconds: parts[3], milliseconds: parts[4] };
291
+ }
292
+ }
293
+ };
294
+ }
295
+ },
296
+
297
+ /** @private */
298
+ _comboBoxValue: {
299
+ type: String,
300
+ observer: '__comboBoxValueChanged'
301
+ },
302
+
303
+ /** @private */
304
+ _inputContainer: Object
305
+ };
306
+ }
307
+
308
+ static get observers() {
309
+ return ['__updateDropdownItems(i18n.*, min, max, step)'];
310
+ }
311
+
312
+ /**
313
+ * Used by `ClearButtonMixin` as a reference to the clear button element.
314
+ * @protected
315
+ * @return {!HTMLElement}
316
+ */
317
+ get clearElement() {
318
+ return this.$.clearButton;
319
+ }
320
+
321
+ /** @protected */
322
+ ready() {
323
+ super.ready();
324
+
325
+ this.addController(
326
+ new InputController(this, (input) => {
327
+ this._setInputElement(input);
328
+ this._setFocusElement(input);
329
+ this.stateTarget = input;
330
+ this.ariaTarget = input;
331
+ })
332
+ );
333
+ this.addController(new AriaLabelController(this.inputElement, this._labelNode));
334
+ this._inputContainer = this.shadowRoot.querySelector('[part~="input-field"]');
335
+ }
336
+
337
+ /**
338
+ * Override method inherited from `InputMixin` to forward the input to combo-box.
339
+ * @protected
340
+ * @override
341
+ */
342
+ _inputElementChanged(input) {
343
+ super._inputElementChanged(input);
344
+
345
+ if (input) {
346
+ this.$.comboBox._setInputElement(input);
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Returns true if the current input value satisfies all constraints (if any).
352
+ * You can override this method for custom validations.
353
+ *
354
+ * @return {boolean} True if the value is valid
355
+ */
356
+ checkValidity() {
357
+ return !!(
358
+ this.inputElement.checkValidity() &&
359
+ (!this.value || this._timeAllowed(this.i18n.parseTime(this.value))) &&
360
+ (!this._comboBoxValue || this.i18n.parseTime(this._comboBoxValue))
361
+ );
362
+ }
363
+
364
+ /**
365
+ * Override method inherited from `FocusMixin` to validate on blur.
366
+ * @param {boolean} focused
367
+ * @protected
368
+ */
369
+ _setFocused(focused) {
370
+ super._setFocused(focused);
371
+
372
+ if (!focused) {
373
+ this.validate();
374
+ }
375
+ }
376
+
377
+ /** @private */
378
+ __validDayDivisor(step) {
379
+ // valid if undefined, or exact divides a day, or has millisecond resolution
380
+ return !step || (24 * 3600) % step === 0 || (step < 1 && ((step % 1) * 1000) % 1 === 0);
381
+ }
382
+
383
+ /**
384
+ * Override an event listener from `ClearButtonMixin`
385
+ * to prevent clearing the input value on Esc key.
386
+ * @param {Event} event
387
+ * @protected
388
+ */
389
+ _onKeyDown(e) {
390
+ if (this.readonly || this.disabled || this.__dropdownItems.length) {
391
+ return;
392
+ }
393
+
394
+ const stepResolution = (this.__validDayDivisor(this.step) && this.step) || 60;
395
+
396
+ if (e.keyCode === 40) {
397
+ this.__onArrowPressWithStep(-stepResolution);
398
+ } else if (e.keyCode === 38) {
399
+ this.__onArrowPressWithStep(stepResolution);
400
+ }
401
+ }
402
+
403
+ /** @private */
404
+ __onArrowPressWithStep(step) {
405
+ const objWithStep = this.__addStep(this.__getMsec(this.__memoValue), step, true);
406
+ this.__memoValue = objWithStep;
407
+ this.inputElement.value = this.i18n.formatTime(this.__validateTime(objWithStep));
408
+ this.__dispatchChange();
409
+ }
410
+
411
+ /** @private */
412
+ __dispatchChange() {
413
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
414
+ }
415
+
416
+ /**
417
+ * Returning milliseconds from Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
418
+ * @private
419
+ */
420
+ __getMsec(obj) {
421
+ let result = ((obj && obj.hours) || 0) * 60 * 60 * 1000;
422
+ result += ((obj && obj.minutes) || 0) * 60 * 1000;
423
+ result += ((obj && obj.seconds) || 0) * 1000;
424
+ result += (obj && parseInt(obj.milliseconds)) || 0;
425
+
426
+ return result;
427
+ }
428
+
429
+ /**
430
+ * Returning seconds from Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
431
+ * @private
432
+ */
433
+ __getSec(obj) {
434
+ let result = ((obj && obj.hours) || 0) * 60 * 60;
435
+ result += ((obj && obj.minutes) || 0) * 60;
436
+ result += (obj && obj.seconds) || 0;
437
+ result += (obj && obj.milliseconds / 1000) || 0;
438
+
439
+ return result;
440
+ }
441
+
442
+ /**
443
+ * Returning Object in the format `{ hours: ..., minutes: ..., seconds: ..., milliseconds: ... }`
444
+ * from the result of adding step value in milliseconds to the milliseconds amount.
445
+ * With `precision` parameter rounding the value to the closest step valid interval.
446
+ * @private
447
+ */
448
+ __addStep(msec, step, precision) {
449
+ // If the time is `00:00` and step changes value downwards, it should be considered as `24:00`
450
+ if (msec === 0 && step < 0) {
451
+ msec = 24 * 60 * 60 * 1000;
452
+ }
453
+
454
+ const stepMsec = step * 1000;
455
+ const diffToNext = msec % stepMsec;
456
+ if (stepMsec < 0 && diffToNext && precision) {
457
+ msec -= diffToNext;
458
+ } else if (stepMsec > 0 && diffToNext && precision) {
459
+ msec -= diffToNext - stepMsec;
460
+ } else {
461
+ msec += stepMsec;
462
+ }
463
+
464
+ var hh = Math.floor(msec / 1000 / 60 / 60);
465
+ msec -= hh * 1000 * 60 * 60;
466
+ var mm = Math.floor(msec / 1000 / 60);
467
+ msec -= mm * 1000 * 60;
468
+ var ss = Math.floor(msec / 1000);
469
+ msec -= ss * 1000;
470
+
471
+ return { hours: hh < 24 ? hh : 0, minutes: mm, seconds: ss, milliseconds: msec };
472
+ }
473
+
474
+ /** @private */
475
+ __updateDropdownItems(i8n, min, max, step) {
476
+ const minTimeObj = this.__validateTime(this.__parseISO(min));
477
+ const minSec = this.__getSec(minTimeObj);
478
+
479
+ const maxTimeObj = this.__validateTime(this.__parseISO(max));
480
+ const maxSec = this.__getSec(maxTimeObj);
481
+
482
+ this.__adjustValue(minSec, maxSec, minTimeObj, maxTimeObj);
483
+
484
+ this.__dropdownItems = this.__generateDropdownList(minSec, maxSec, step);
485
+
486
+ if (step !== this.__oldStep) {
487
+ this.__oldStep = step;
488
+ const parsedObj = this.__validateTime(this.__parseISO(this.value));
489
+ this.__updateValue(parsedObj);
490
+ }
491
+
492
+ if (this.value) {
493
+ this._comboBoxValue = this.i18n.formatTime(this.i18n.parseTime(this.value));
494
+ }
495
+ }
496
+
497
+ /** @private */
498
+ __generateDropdownList(minSec, maxSec, step) {
499
+ if (step < 15 * 60 || !this.__validDayDivisor(step)) {
500
+ return [];
501
+ }
502
+
503
+ const generatedList = [];
504
+
505
+ // Default step in overlay items is 1 hour
506
+ step = step || 3600;
507
+
508
+ let time = -step + minSec;
509
+ while (time + step >= minSec && time + step <= maxSec) {
510
+ const timeObj = this.__validateTime(this.__addStep(time * 1000, step));
511
+ time += step;
512
+ const formatted = this.i18n.formatTime(timeObj);
513
+ generatedList.push({ label: formatted, value: formatted });
514
+ }
515
+
516
+ return generatedList;
517
+ }
518
+
519
+ /** @private */
520
+ __adjustValue(minSec, maxSec, minTimeObj, maxTimeObj) {
521
+ // Do not change the value if it is empty
522
+ if (!this.__memoValue) {
523
+ return;
524
+ }
525
+
526
+ const valSec = this.__getSec(this.__memoValue);
527
+
528
+ if (valSec < minSec) {
529
+ this.__updateValue(minTimeObj);
530
+ } else if (valSec > maxSec) {
531
+ this.__updateValue(maxTimeObj);
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Override an observer from `InputMixin`.
537
+ * @protected
538
+ * @override
539
+ */
540
+ _valueChanged(value, oldValue) {
541
+ const parsedObj = (this.__memoValue = this.__parseISO(value));
542
+ const newValue = this.__formatISO(parsedObj) || '';
543
+
544
+ if (this.value !== '' && this.value !== null && !parsedObj) {
545
+ this.value = oldValue;
546
+ } else if (this.value !== newValue) {
547
+ this.value = newValue;
548
+ } else {
549
+ this.__updateInputValue(parsedObj);
550
+ }
551
+
552
+ this._toggleHasValue(!!this.value);
553
+ }
554
+
555
+ /** @private */
556
+ __comboBoxValueChanged(value, oldValue) {
557
+ if (value === '' && oldValue === undefined) {
558
+ return;
559
+ }
560
+
561
+ const parsedObj = this.i18n.parseTime(value);
562
+ const newValue = this.i18n.formatTime(parsedObj) || '';
563
+
564
+ if (parsedObj) {
565
+ if (value !== newValue) {
566
+ this._comboBoxValue = newValue;
567
+ } else {
568
+ this.__updateValue(parsedObj);
569
+ }
570
+ } else {
571
+ this.value = '';
572
+ }
573
+ }
574
+
575
+ /** @private */
576
+ __onChange(event) {
577
+ event.stopPropagation();
578
+
579
+ this.validate();
580
+
581
+ this.__dispatchChange();
582
+ }
583
+
584
+ /** @private */
585
+ __updateValue(obj) {
586
+ const timeString = this.__formatISO(this.__validateTime(obj)) || '';
587
+ this.value = timeString;
588
+ }
589
+
590
+ /** @private */
591
+ __updateInputValue(obj) {
592
+ const timeString = this.i18n.formatTime(this.__validateTime(obj)) || '';
593
+ this._comboBoxValue = timeString;
594
+ }
595
+
596
+ /** @private */
597
+ __validateTime(timeObject) {
598
+ if (timeObject) {
599
+ timeObject.hours = parseInt(timeObject.hours);
600
+ timeObject.minutes = parseInt(timeObject.minutes || 0);
601
+ timeObject.seconds = this.__stepSegment < 3 ? undefined : parseInt(timeObject.seconds || 0);
602
+ timeObject.milliseconds = this.__stepSegment < 4 ? undefined : parseInt(timeObject.milliseconds || 0);
603
+ }
604
+ return timeObject;
605
+ }
606
+
607
+ /** @private */
608
+ get __stepSegment() {
609
+ if (this.step % 3600 === 0) {
610
+ // Accept hours
611
+ return 1;
612
+ } else if (this.step % 60 === 0 || !this.step) {
613
+ // Accept minutes
614
+ return 2;
615
+ } else if (this.step % 1 === 0) {
616
+ // Accept seconds
617
+ return 3;
618
+ } else if (this.step < 1) {
619
+ // Accept milliseconds
620
+ return 4;
621
+ }
622
+ return undefined;
623
+ }
624
+
625
+ /** @private */
626
+ __formatISO(time) {
627
+ // The default i18n formatter implementation is ISO 8601 compliant
628
+ return TimePicker.properties.i18n.value().formatTime(time);
629
+ }
630
+
631
+ /** @private */
632
+ __parseISO(text) {
633
+ // The default i18n parser implementation is ISO 8601 compliant
634
+ return TimePicker.properties.i18n.value().parseTime(text);
635
+ }
636
+
637
+ /**
638
+ * Returns true if `time` satisfies the `min` and `max` constraints (if any).
639
+ *
640
+ * @param {!TimePickerTime} time Value to check against constraints
641
+ * @return {boolean} True if `time` satisfies the constraints
642
+ * @protected
643
+ */
644
+ _timeAllowed(time) {
645
+ const parsedMin = this.i18n.parseTime(this.min);
646
+ const parsedMax = this.i18n.parseTime(this.max);
647
+
648
+ return (
649
+ (!this.__getMsec(parsedMin) || this.__getMsec(time) >= this.__getMsec(parsedMin)) &&
650
+ (!this.__getMsec(parsedMax) || this.__getMsec(time) <= this.__getMsec(parsedMax))
651
+ );
652
+ }
653
+
654
+ /**
655
+ * Override method inherited from `ClearButtonMixin`.
656
+ * @protected
657
+ */
658
+ _onClearButtonClick() {}
659
+
660
+ /**
661
+ * Override method inherited from `InputConstraintsMixin`.
662
+ * @protected
663
+ */
664
+ _onChange() {}
665
+
666
+ /**
667
+ * Override method inherited from `InputMixin`.
668
+ * @protected
669
+ */
670
+ _onInput() {
671
+ // Need to invoke _checkInputValue from PatternMixin to prevent invalid input
672
+ this._checkInputValue();
673
+ }
674
+
675
+ /**
676
+ * Fired when the user commits a value change.
677
+ *
678
+ * @event change
679
+ */
680
+ }
681
+
682
+ customElements.define(TimePicker.is, TimePicker);
683
+
684
+ export { TimePicker };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
7
+ import '@vaadin/vaadin-lumo-styles/font-icons.js';
8
+ import { inputFieldShared } from '@vaadin/vaadin-lumo-styles/mixins/input-field-shared.js';
9
+
10
+ const timePicker = css`
11
+ [part~='toggle-button']::before {
12
+ content: var(--lumo-icons-clock);
13
+ }
14
+
15
+ :host([dir='rtl']) [part='input-field'] ::slotted(input:placeholder-shown) {
16
+ --_lumo-text-field-overflow-mask-image: none;
17
+ }
18
+
19
+ :host([dir='rtl']) [part='input-field'] ::slotted(input) {
20
+ --_lumo-text-field-overflow-mask-image: linear-gradient(to left, transparent, #000 1.25em);
21
+ }
22
+ `;
23
+
24
+ registerStyles('vaadin-time-picker', [inputFieldShared, timePicker], { moduleId: 'lumo-time-picker' });