/**
* MenuLayouts combine a menu and a content {@link OO.ui.PanelLayout panel}. The menu is positioned
* relative to the content (after, before, top, or bottom) and its size is customized with the
* #menuSize config. The content area will fill all remaining space.
*
* If menu size needs to be overridden, it can be accomplished using CSS similar to the snippet
* below. MenuLayout's CSS will override the appropriate values with 'auto' or '0' to display the
* menu correctly. If `menuPosition` is known beforehand, CSS rules corresponding to other positions
* may be omitted.
*
* .oo-ui-menuLayout-menu {
* width: 200px;
* height: 200px;
* }
*
* .oo-ui-menuLayout-content {
* top: 200px;
* left: 200px;
* right: 200px;
* bottom: 200px;
* }
*
* @example
* let menuLayout;
* const menuPanel = new OO.ui.PanelLayout( {
* padded: true,
* expanded: true,
* scrollable: true
* } ),
* contentPanel = new OO.ui.PanelLayout( {
* padded: true,
* expanded: true,
* scrollable: true
* } ),
* select = new OO.ui.SelectWidget( {
* items: [
* new OO.ui.OptionWidget( {
* data: 'before',
* label: 'Before'
* } ),
* new OO.ui.OptionWidget( {
* data: 'after',
* label: 'After'
* } ),
* new OO.ui.OptionWidget( {
* data: 'top',
* label: 'Top'
* } ),
* new OO.ui.OptionWidget( {
* data: 'bottom',
* label: 'Bottom'
* } )
* ]
* } ).on( 'select', function ( item ) {
* menuLayout.setMenuPosition( item.getData() );
* } );
*
* menuLayout = new OO.ui.MenuLayout( {
* position: 'top',
* menuPanel: menuPanel,
* contentPanel: contentPanel
* } );
* menuLayout.$menu.append(
* menuPanel.$element.append( '<b>Menu panel</b>', select.$element )
* );
* menuLayout.$content.append(
* contentPanel.$element.append(
* '<b>Content panel</b>',
* '<p>Note that the menu is positioned relative to the content panel: ' +
* 'top, bottom, after, before.</p>'
* )
* );
* $( document.body ).append( menuLayout.$element );
*
* @class
* @extends OO.ui.Layout
*
* @constructor
* @param {Object} [config] Configuration options
* @param {OO.ui.PanelLayout} [config.menuPanel] Menu panel
* @param {OO.ui.PanelLayout} [config.contentPanel] Content panel
* @param {boolean} [config.expanded=true] Expand the layout to fill the entire parent element.
* @param {boolean} [config.showMenu=true] Show menu
* @param {string} [config.menuPosition='before'] Position of menu: `top`, `after`, `bottom` or `before`
*/
OO.ui.MenuLayout = function OoUiMenuLayout( config ) {
// Configuration initialization
config = Object.assign( {
expanded: true,
showMenu: true,
menuPosition: 'before'
}, config );
// Parent constructor
OO.ui.MenuLayout.super.call( this, config );
this.menuPanel = null;
this.contentPanel = null;
this.expanded = !!config.expanded;
/**
* Menu DOM node
*
* @property {jQuery}
*/
this.$menu = $( '<div>' );
/**
* Content DOM node
*
* @property {jQuery}
*/
this.$content = $( '<div>' );
// Initialization
this.$menu.addClass( 'oo-ui-menuLayout-menu' );
this.$content.addClass( 'oo-ui-menuLayout-content' );
this.$element.addClass( 'oo-ui-menuLayout' );
if ( config.expanded ) {
this.$element.addClass( 'oo-ui-menuLayout-expanded' );
} else {
this.$element.addClass( 'oo-ui-menuLayout-static' );
}
if ( config.menuPanel ) {
this.setMenuPanel( config.menuPanel );
}
if ( config.contentPanel ) {
this.setContentPanel( config.contentPanel );
}
this.setMenuPosition( config.menuPosition );
this.toggleMenu( config.showMenu );
};
/* Setup */
OO.inheritClass( OO.ui.MenuLayout, OO.ui.Layout );
/* Methods */
/**
* Toggle menu.
*
* @param {boolean} [showMenu] Show menu, omit to toggle
* @chainable
* @return {OO.ui.MenuLayout} The layout, for chaining
*/
OO.ui.MenuLayout.prototype.toggleMenu = function ( showMenu ) {
showMenu = showMenu === undefined ? !this.showMenu : !!showMenu;
if ( this.showMenu !== showMenu ) {
this.showMenu = showMenu;
this.$element
.toggleClass( 'oo-ui-menuLayout-showMenu', this.showMenu )
.toggleClass( 'oo-ui-menuLayout-hideMenu', !this.showMenu );
this.$menu.attr( 'aria-hidden', this.showMenu ? 'false' : 'true' );
}
return this;
};
/**
* Check if menu is visible
*
* @return {boolean} Menu is visible
*/
OO.ui.MenuLayout.prototype.isMenuVisible = function () {
return this.showMenu;
};
/**
* Set menu position.
*
* @param {string} position Position of menu, either `top`, `after`, `bottom` or `before`
* @chainable
* @return {OO.ui.MenuLayout} The layout, for chaining
*/
OO.ui.MenuLayout.prototype.setMenuPosition = function ( position ) {
if ( [ 'top', 'bottom', 'before', 'after' ].indexOf( position ) === -1 ) {
position = 'before';
}
this.$element.removeClass( 'oo-ui-menuLayout-' + this.menuPosition );
this.menuPosition = position;
if ( this.menuPosition === 'top' || this.menuPosition === 'before' ) {
this.$element.append( this.$menu, this.$content );
} else {
this.$element.append( this.$content, this.$menu );
}
this.$element.addClass( 'oo-ui-menuLayout-' + position );
return this;
};
/**
* Get menu position.
*
* @return {string} Menu position
*/
OO.ui.MenuLayout.prototype.getMenuPosition = function () {
return this.menuPosition;
};
/**
* Set the menu panel.
*
* @param {OO.ui.PanelLayout} menuPanel Menu panel
*/
OO.ui.MenuLayout.prototype.setMenuPanel = function ( menuPanel ) {
this.menuPanel = menuPanel;
this.$menu.append( this.menuPanel.$element );
};
/**
* Set the content panel.
*
* @param {OO.ui.PanelLayout} contentPanel Content panel
*/
OO.ui.MenuLayout.prototype.setContentPanel = function ( contentPanel ) {
this.contentPanel = contentPanel;
this.$content.append( this.contentPanel.$element );
};
/**
* Clear the menu panel.
*/
OO.ui.MenuLayout.prototype.clearMenuPanel = function () {
this.menuPanel = null;
this.$menu.empty();
};
/**
* Clear the content panel.
*/
OO.ui.MenuLayout.prototype.clearContentPanel = function () {
this.contentPanel = null;
this.$content.empty();
};
/**
* Reset the scroll offset of all panels and the tab select widget
*
* @inheritdoc
*/
OO.ui.MenuLayout.prototype.resetScroll = function () {
if ( this.menuPanel ) {
this.menuPanel.resetScroll();
}
if ( this.contentPanel ) {
this.contentPanel.resetScroll();
}
return this;
};