@@ -3,6 +3,7 @@ import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
3
3
import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js" ;
4
4
import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js" ;
5
5
import DateRangePickerTemplate from "./generated/templates/DateRangePickerTemplate.lit.js" ;
6
+ import RenderScheduler from "../../base/src/RenderScheduler.js" ;
6
7
7
8
// Styles
8
9
import DateRangePickerCss from "./generated/themes/DateRangePicker.css.js" ;
@@ -64,6 +65,25 @@ const metadata = {
64
65
*
65
66
* <code>import @ui5/webcomponents/dist/DateRangePicker.js";</code>
66
67
*
68
+ * <h3>Keyboard Handling</h3>
69
+ * The <code>ui5-daterange-picker</code> provides advanced keyboard handling.
70
+ * <br>
71
+ *
72
+ * When the <code>ui5-daterange-picker</code> input field is focused the user can
73
+ * increment or decrement the corresponding field of the JS date object referenced by <code>_firstDateTimestamp</code> propery
74
+ * if the caret symbol is before the delimiter character or <code>_lastDateTimestamp</code> property if the caret symbol is
75
+ * after the delimiter character.
76
+ * The following shortcuts are enabled:
77
+ * <br>
78
+ * <ul>
79
+ * <li>[PAGEDOWN] - Decrements the corresponding day of the month by one</li>
80
+ * <li>[SHIFT] + [PAGEDOWN] - Decrements the corresponding month by one</li>
81
+ * <li>[SHIFT] + [CTRL] + [PAGEDOWN] - Decrements the corresponding year by one</li>
82
+ * <li>[PAGEUP] - Increments the corresponding day of the month by one</li>
83
+ * <li>[SHIFT] + [PAGEUP] - Increments the corresponding month by one</li>
84
+ * <li>[SHIFT] + [CTRL] + [PAGEUP] - Increments the corresponding year by one</li>
85
+ * </ul>
86
+ *
67
87
* @constructor
68
88
* @author SAP SE
69
89
* @alias sap.ui.webcomponents.main.DateRangePicker
@@ -204,7 +224,8 @@ class DateRangePicker extends DatePicker {
204
224
}
205
225
206
226
this . _calendar . selectedDates = this . dateIntervalArrayBuilder ( this . _firstDateTimestamp * 1000 , this . _lastDateTimestamp * 1000 ) ;
207
- this . value = this . _formatValue ( this . _firstDateTimestamp , this . _lastDateTimestamp ) ;
227
+
228
+ this . value = this . _formatValue ( firstDate . valueOf ( ) / 1000 , secondDate . valueOf ( ) / 1000 ) ;
208
229
this . realValue = this . value ;
209
230
this . _prevValue = this . realValue ;
210
231
}
@@ -378,6 +399,125 @@ class DateRangePicker extends DatePicker {
378
399
}
379
400
}
380
401
402
+ /**
403
+ * Adds or extracts a given number of measuring units from the "dateValue" property value
404
+ *
405
+ * @param {boolean } forward if true indicates addition
406
+ * @param {boolean } years indicates that the measuring unit is in years
407
+ * @param {boolean } months indicates that the measuring unit is in months
408
+ * @param {boolean } days indicates that the measuring unit is in days
409
+ * @param {int } step number of measuring units to substract or add defaults ot 1
410
+ */
411
+ async _changeDateValueWrapper ( forward , years , months , days , step = 1 ) {
412
+ const emptyValue = this . value === "" ;
413
+ const isValid = emptyValue || this . _checkValueValidity ( this . value ) ;
414
+
415
+ if ( ! isValid ) {
416
+ return ;
417
+ }
418
+
419
+ const dates = this . _splitValueByDelimiter ( this . value ) ;
420
+ const innerInput = this . shadowRoot . querySelector ( "ui5-input" ) . shadowRoot . querySelector ( ".ui5-input-inner" ) ;
421
+ const caretPos = this . _getCaretPosition ( innerInput ) ;
422
+ const first = dates [ 0 ] && caretPos <= dates [ 0 ] . trim ( ) . length + 1 ;
423
+ const last = dates [ 1 ] && ( caretPos >= this . value . length - dates [ 1 ] . trim ( ) . length - 1 && caretPos <= this . value . length ) ;
424
+ let firstDate = this . getFormat ( ) . parse ( dates [ 0 ] ) ;
425
+ let lastDate = this . getFormat ( ) . parse ( dates [ 1 ] ) ;
426
+
427
+ if ( first && firstDate ) {
428
+ firstDate = this . _changeDateValue ( firstDate , forward , years , months , days , step ) ;
429
+ } else if ( last && lastDate ) {
430
+ lastDate = this . _changeDateValue ( lastDate , forward , years , months , days , step ) ;
431
+ }
432
+
433
+ this . value = this . _formatValue ( firstDate . valueOf ( ) / 1000 , lastDate . valueOf ( ) / 1000 ) ;
434
+
435
+ await RenderScheduler . whenFinished ( ) ;
436
+ // Return the caret on the previous position after rendering
437
+ this . _setCaretPosition ( innerInput , caretPos ) ;
438
+ }
439
+
440
+ /**
441
+ * This method is used in the derived classes
442
+ */
443
+ async _handleEnterPressed ( ) {
444
+ const innerInput = this . shadowRoot . querySelector ( "ui5-input" ) . shadowRoot . querySelector ( ".ui5-input-inner" ) ;
445
+ const caretPos = this . _getCaretPosition ( innerInput ) ;
446
+
447
+ this . _confirmInput ( ) ;
448
+
449
+ await RenderScheduler . whenFinished ( ) ;
450
+ // Return the caret on the previous position after rendering
451
+ this . _setCaretPosition ( innerInput , caretPos ) ;
452
+ }
453
+
454
+ _onfocusout ( ) {
455
+ this . _confirmInput ( ) ;
456
+ }
457
+
458
+ _confirmInput ( ) {
459
+ const emptyValue = this . value === "" ;
460
+
461
+ if ( emptyValue ) {
462
+ return ;
463
+ }
464
+
465
+ const dates = this . _splitValueByDelimiter ( this . value ) ;
466
+ let firstDate = this . getFormat ( ) . parse ( dates [ 0 ] ) ;
467
+ let lastDate = this . getFormat ( ) . parse ( dates [ 1 ] ) ;
468
+
469
+ if ( firstDate > lastDate ) {
470
+ const temp = firstDate ;
471
+ firstDate = lastDate ;
472
+ lastDate = temp ;
473
+ }
474
+
475
+ const newValue = this . _formatValue ( firstDate . valueOf ( ) / 1000 , lastDate . valueOf ( ) / 1000 ) ;
476
+
477
+ this . _setValue ( newValue ) ;
478
+ }
479
+
480
+ /**
481
+ * Returns the caret (cursor) position of the specified text field (field).
482
+ * Return value range is 0-field.value.length.
483
+ */
484
+ _getCaretPosition ( field ) {
485
+ // Initialize
486
+ let caretPos = 0 ;
487
+
488
+ // IE Support
489
+ if ( document . selection ) {
490
+ // Set focus on the element
491
+ field . focus ( ) ;
492
+
493
+ // To get cursor position, get empty selection range
494
+ const selection = document . selection . createRange ( ) ;
495
+
496
+ // Move selection start to 0 position
497
+ selection . moveStart ( "character" , - field . value . length ) ;
498
+
499
+ // The caret position is selection length
500
+ caretPos = selection . text . length ;
501
+ } else if ( field . selectionStart || field . selectionStart === "0" ) { // Firefox support
502
+ caretPos = field . selectionDirection === "backward" ? field . selectionStart : field . selectionEnd ;
503
+ }
504
+
505
+ return caretPos ;
506
+ }
507
+
508
+ _setCaretPosition ( field , caretPos ) {
509
+ if ( field . createTextRange ) {
510
+ const range = field . createTextRange ( ) ;
511
+ range . move ( "character" , caretPos ) ;
512
+ range . select ( ) ;
513
+ } else if ( field . selectionStart ) {
514
+ field . focus ( ) ;
515
+ field . setSelectionRange ( caretPos , caretPos ) ;
516
+ } else {
517
+ field . focus ( ) ;
518
+ }
519
+ }
520
+
381
521
_handleCalendarSelectedDatesChange ( ) {
382
522
this . _updateValueCalendarSelectedDatesChange ( ) ;
383
523
this . _cleanHoveredAttributeFromVisibleItems ( ) ;
@@ -409,23 +549,31 @@ class DateRangePicker extends DatePicker {
409
549
}
410
550
411
551
_updateValueCalendarSelectedDatesChange ( ) {
552
+ const calStartDate = CalendarDate . fromTimestamp ( this . _firstDateTimestamp * 1000 , this . _primaryCalendarType ) ;
553
+ const calEndDate = CalendarDate . fromTimestamp ( this . _lastDateTimestamp * 1000 , this . _primaryCalendarType ) ;
554
+
412
555
// Collect both dates and merge them into one
413
556
if ( this . _firstDateTimestamp !== this . _lastDateTimestamp || this . _oneTimeStampSelected ) {
414
- this . value = this . _formatValue ( this . _firstDateTimestamp , this . _lastDateTimestamp ) ;
557
+ this . value = this . _formatValue ( calStartDate . toLocalJSDate ( ) . valueOf ( ) / 1000 , calEndDate . toLocalJSDate ( ) . valueOf ( ) / 1000 ) ;
415
558
}
416
559
417
- this . realValue = this . _formatValue ( this . _firstDateTimestamp , this . _lastDateTimestamp ) ;
560
+ this . realValue = this . _formatValue ( calStartDate . toLocalJSDate ( ) . valueOf ( ) / 1000 , calEndDate . toLocalJSDate ( ) . valueOf ( ) / 1000 ) ;
418
561
this . _prevValue = this . realValue ;
419
562
}
420
563
564
+ /**
565
+ * Combines the start and end dates of a range into a formated string
566
+ *
567
+ * @param {int } firstDateValue locale start date timestamp
568
+ * @param {int } lastDateValue locale end date timestamp
569
+ * @returns {string } formated start to end date range
570
+ */
421
571
_formatValue ( firstDateValue , lastDateValue ) {
422
572
let value = "" ;
423
573
const delimiter = this . delimiter ,
424
574
format = this . getFormat ( ) ,
425
- firstDate = new Date ( firstDateValue * 1000 ) ,
426
- lastDate = new Date ( lastDateValue * 1000 ) ,
427
- firstDateString = format . format ( new Date ( firstDate . getUTCFullYear ( ) , firstDate . getUTCMonth ( ) , firstDate . getUTCDate ( ) , firstDate . getUTCHours ( ) ) ) ,
428
- lastDateString = format . format ( new Date ( lastDate . getUTCFullYear ( ) , lastDate . getUTCMonth ( ) , lastDate . getUTCDate ( ) , lastDate . getUTCHours ( ) ) ) ;
575
+ firstDateString = format . format ( new Date ( firstDateValue * 1000 ) ) ,
576
+ lastDateString = format . format ( new Date ( lastDateValue * 1000 ) ) ;
429
577
430
578
if ( firstDateValue ) {
431
579
if ( delimiter && delimiter !== "" && lastDateString ) {
0 commit comments