From: Bartosz DziewoƄski Date: Wed, 16 Sep 2015 15:22:35 +0000 (+0200) Subject: mw.widgets.DateInputWidget: Implement $overlay config option X-Git-Tag: 1.31.0-rc.0~9680^2 X-Git-Url: https://git.cyclocoop.org/%28%28?a=commitdiff_plain;h=7133d749c23b32ab72ea7201c24d8a66e9871da8;p=lhc%2Fweb%2Fwiklou.git mw.widgets.DateInputWidget: Implement $overlay config option Unlike in other widgets with popups, the one here (CalendarWidget) is focusable. As the text input and calendar are not in DOM order when using $overlay, we fix up focus transitions between them manually. Bug: T112676 Change-Id: I504f81f843e8328f609824d44f276296645d5dd1 --- diff --git a/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js index 9535481d4b..d5e5b960c8 100644 --- a/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js +++ b/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js @@ -15,6 +15,7 @@ * @class * @extends OO.ui.Widget * @mixins OO.ui.mixin.TabIndexedElement + * @mixins OO.ui.mixin.FloatableElement * * @constructor * @param {Object} [config] Configuration options @@ -32,6 +33,7 @@ // Mixin constructors OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$element } ) ); + OO.ui.mixin.FloatableElement.call( this, config ); // Properties this.precision = config.precision || 'day'; @@ -98,6 +100,7 @@ OO.inheritClass( mw.widgets.CalendarWidget, OO.ui.Widget ); OO.mixinClass( mw.widgets.CalendarWidget, OO.ui.mixin.TabIndexedElement ); + OO.mixinClass( mw.widgets.CalendarWidget, OO.ui.mixin.FloatableElement ); /* Events */ @@ -537,4 +540,18 @@ } }; + /** + * @inheritdoc + */ + mw.widgets.CalendarWidget.prototype.toggle = function ( visible ) { + // Parent method + mw.widgets.CalendarWidget.parent.prototype.toggle.call( this, visible ); + + if ( this.$floatableContainer ) { + this.togglePositioning( this.isVisible() ); + } + + return this; + }; + }( jQuery, mediaWiki ) ); diff --git a/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js index 657d9c540a..dabe25a226 100644 --- a/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js +++ b/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js @@ -78,6 +78,10 @@ * @cfg {boolean} [required=false] Mark the field as required. Implies `indicator: 'required'`. * @cfg {string} [mustBeAfter] Validates the date to be after this. In the 'YYYY-MM-DD' format. * @cfg {string} [mustBeBefore] Validates the date to be before this. In the 'YYYY-MM-DD' format. + * @cfg {jQuery} [$overlay] Render the calendar into a separate layer. This configuration is + * useful in cases where the expanded calendar is larger than its container. The specified + * overlay layer is usually on top of the container and has a larger area. By default, the + * calendar uses relative positioning. */ mw.widgets.DateInputWidget = function MWWDateInputWidget( config ) { // Config initialization @@ -106,6 +110,8 @@ validate: this.validateDate.bind( this ) } ); this.calendar = new mw.widgets.CalendarWidget( { + // Can't pass `$floatableContainer: this.$element` here, the latter is not set yet. + // Instead we call setFloatableContainer() below. precision: config.precision } ); this.inCalendar = 0; @@ -161,9 +167,44 @@ this.setTabIndexedElement( this.handle.$element ); this.handle.$element .addClass( 'mw-widget-dateInputWidget-handle' ); + this.calendar.$element + .addClass( 'mw-widget-dateInputWidget-calendar' ); this.$element .addClass( 'mw-widget-dateInputWidget' ) .append( this.handle.$element, this.textInput.$element, this.calendar.$element ); + + if ( config.$overlay ) { + this.calendar.setFloatableContainer( this.$element ); + config.$overlay.append( this.calendar.$element ); + + // The text input and calendar are not in DOM order, so fix up focus transitions. + this.textInput.$input.on( 'keydown', function ( e ) { + if ( e.which === OO.ui.Keys.TAB ) { + if ( e.shiftKey ) { + // Tabbing backward from text input: normal browser behavior + $.noop(); + } else { + // Tabbing forward from text input: just focus the calendar + this.calendar.$element.focus(); + return false; + } + } + }.bind( this ) ); + this.calendar.$element.on( 'keydown', function ( e ) { + if ( e.which === OO.ui.Keys.TAB ) { + if ( e.shiftKey ) { + // Tabbing backward from calendar: just focus the text input + this.textInput.$input.focus(); + return false; + } else { + // Tabbing forward from calendar: focus the text input, then allow normal browser + // behavior to move focus to next focusable after it + this.textInput.$input.focus(); + } + } + }.bind( this ) ); + } + // Set handle label and hide stuff this.updateUI(); this.deactivate(); @@ -261,7 +302,11 @@ setTimeout( function () { var $focussed = $( ':focus' ); // Deactivate unless the focus moved to something else inside this widget - if ( !OO.ui.contains( widget.$element[ 0 ], $focussed[ 0 ], true ) ) { + if ( + !OO.ui.contains( widget.$element[ 0 ], $focussed[ 0 ], true ) && + // Calendar might be in an $overlay + !OO.ui.contains( widget.calendar.$element[ 0 ], $focussed[ 0 ], true ) + ) { widget.deactivate(); } }, 0 ); diff --git a/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less b/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less index 8c1d2a0016..bc8ab22da9 100644 --- a/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less +++ b/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less @@ -43,7 +43,7 @@ cursor: default; } - > .mw-widget-calendarWidget { + &-calendar { position: absolute; z-index: 1; } @@ -70,7 +70,7 @@ z-index: 2; } - > .mw-widget-calendarWidget { + &-calendar { background-color: white; margin-top: -2px;