diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:30:02 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:30:02 -0400 |
commit | 1de335ad3f395ca6861085393ba366a9e3fb4a0d (patch) | |
tree | f1fdd326034e05177596851be6a7127615d81498 /vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js | |
parent | 9c75fa8ff6d4d38ef552c00fef5969fb154765e8 (diff) | |
parent | f6d65e533c62f6deb21342d4901ece24497b433e (diff) |
Merge commit 'f6d65'
# Conflicts:
# skins/ArchLinux/ArchLinux.php
Diffstat (limited to 'vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js')
-rw-r--r-- | vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js b/vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js new file mode 100644 index 00000000..a1755edd --- /dev/null +++ b/vendor/oojs/oojs-ui/src/widgets/MenuSelectWidget.js @@ -0,0 +1,254 @@ +/** + * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and + * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget. + * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxWidget ComboBoxWidget}, + * and {@link OO.ui.LookupElement LookupElement} for examples of widgets that contain menus. + * MenuSelectWidgets themselves are not instantiated directly, rather subclassed + * and customized to be opened, closed, and displayed as needed. + * + * By default, menus are clipped to the visible viewport and are not visible when a user presses the + * mouse outside the menu. + * + * Menus also have support for keyboard interaction: + * + * - Enter/Return key: choose and select a menu option + * - Up-arrow key: highlight the previous menu option + * - Down-arrow key: highlight the next menu option + * - Esc key: hide the menu + * + * Please see the [OOjs UI documentation on MediaWiki][1] for more information. + * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options + * + * @class + * @extends OO.ui.SelectWidget + * @mixins OO.ui.ClippableElement + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match + * the text the user types. This config is used by {@link OO.ui.ComboBoxWidget ComboBoxWidget} + * and {@link OO.ui.LookupElement LookupElement} + * @cfg {OO.ui.Widget} [widget] Widget associated with the menu’s active state. If the user clicks the mouse + * anywhere on the page outside of this widget, the menu is hidden. + * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu. + */ +OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) { + // Configuration initialization + config = config || {}; + + // Parent constructor + OO.ui.MenuSelectWidget.super.call( this, config ); + + // Mixin constructors + OO.ui.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) ); + + // Properties + this.newItems = null; + this.autoHide = config.autoHide === undefined || !!config.autoHide; + this.$input = config.input ? config.input.$input : null; + this.$widget = config.widget ? config.widget.$element : null; + this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this ); + + // Initialization + this.$element + .addClass( 'oo-ui-menuSelectWidget' ) + .attr( 'role', 'menu' ); + + // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods + // that reference properties not initialized at that time of parent class construction + // TODO: Find a better way to handle post-constructor setup + this.visible = false; + this.$element.addClass( 'oo-ui-element-hidden' ); +}; + +/* Setup */ + +OO.inheritClass( OO.ui.MenuSelectWidget, OO.ui.SelectWidget ); +OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.ClippableElement ); + +/* Methods */ + +/** + * Handles document mouse down events. + * + * @protected + * @param {jQuery.Event} e Key down event + */ +OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) { + if ( + !OO.ui.contains( this.$element[ 0 ], e.target, true ) && + ( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) ) + ) { + this.toggle( false ); + } +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) { + var currentItem = this.getHighlightedItem() || this.getSelectedItem(); + + if ( !this.isDisabled() && this.isVisible() ) { + switch ( e.keyCode ) { + case OO.ui.Keys.LEFT: + case OO.ui.Keys.RIGHT: + // Do nothing if a text field is associated, arrow keys will be handled natively + if ( !this.$input ) { + OO.ui.MenuSelectWidget.super.prototype.onKeyDown.call( this, e ); + } + break; + case OO.ui.Keys.ESCAPE: + case OO.ui.Keys.TAB: + if ( currentItem ) { + currentItem.setHighlighted( false ); + } + this.toggle( false ); + // Don't prevent tabbing away, prevent defocusing + if ( e.keyCode === OO.ui.Keys.ESCAPE ) { + e.preventDefault(); + e.stopPropagation(); + } + break; + default: + OO.ui.MenuSelectWidget.super.prototype.onKeyDown.call( this, e ); + return; + } + } +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.bindKeyDownListener = function () { + if ( this.$input ) { + this.$input.on( 'keydown', this.onKeyDownHandler ); + } else { + OO.ui.MenuSelectWidget.super.prototype.bindKeyDownListener.call( this ); + } +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () { + if ( this.$input ) { + this.$input.off( 'keydown', this.onKeyDownHandler ); + } else { + OO.ui.MenuSelectWidget.super.prototype.unbindKeyDownListener.call( this ); + } +}; + +/** + * Choose an item. + * + * When a user chooses an item, the menu is closed. + * + * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard + * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method. + * @param {OO.ui.OptionWidget} item Item to choose + * @chainable + */ +OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) { + OO.ui.MenuSelectWidget.super.prototype.chooseItem.call( this, item ); + this.toggle( false ); + return this; +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.addItems = function ( items, index ) { + var i, len, item; + + // Parent method + OO.ui.MenuSelectWidget.super.prototype.addItems.call( this, items, index ); + + // Auto-initialize + if ( !this.newItems ) { + this.newItems = []; + } + + for ( i = 0, len = items.length; i < len; i++ ) { + item = items[ i ]; + if ( this.isVisible() ) { + // Defer fitting label until item has been attached + item.fitLabel(); + } else { + this.newItems.push( item ); + } + } + + // Reevaluate clipping + this.clip(); + + return this; +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.removeItems = function ( items ) { + // Parent method + OO.ui.MenuSelectWidget.super.prototype.removeItems.call( this, items ); + + // Reevaluate clipping + this.clip(); + + return this; +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.clearItems = function () { + // Parent method + OO.ui.MenuSelectWidget.super.prototype.clearItems.call( this ); + + // Reevaluate clipping + this.clip(); + + return this; +}; + +/** + * @inheritdoc + */ +OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) { + visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length; + + var i, len, + change = visible !== this.isVisible(); + + // Parent method + OO.ui.MenuSelectWidget.super.prototype.toggle.call( this, visible ); + + if ( change ) { + if ( visible ) { + this.bindKeyDownListener(); + + if ( this.newItems && this.newItems.length ) { + for ( i = 0, len = this.newItems.length; i < len; i++ ) { + this.newItems[ i ].fitLabel(); + } + this.newItems = null; + } + this.toggleClipping( true ); + + // Auto-hide + if ( this.autoHide ) { + this.getElementDocument().addEventListener( + 'mousedown', this.onDocumentMouseDownHandler, true + ); + } + } else { + this.unbindKeyDownListener(); + this.getElementDocument().removeEventListener( + 'mousedown', this.onDocumentMouseDownHandler, true + ); + this.toggleClipping( false ); + } + } + + return this; +}; |