2015-08-17 17:00:26 -07:00
/ * *
* @ file
* Define vertical tabs functionality .
* /
/ * *
* Triggers when form values inside a vertical tab changes .
*
* This is used to update the summary in vertical tabs in order to know what
* are the important fields ' values .
*
* @ event summaryUpdated
* /
( function ( $ ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
/ * *
* This script transforms a set of details into a stack of vertical tabs .
*
* Each tab may have a summary which can be updated by another
* script . For that to work , each details element has an associated
* 'verticalTabCallback' ( with jQuery . data ( ) attached to the details ) ,
* which is called every time the user performs an update to a form
* element inside the tab pane .
*
* @ type { Drupal ~ behavior }
* /
Drupal . behaviors . verticalTabs = {
attach : function ( context ) {
2015-10-08 11:40:12 -07:00
var width = drupalSettings . widthBreakpoint || 640 ;
var mq = '(max-width: ' + width + 'px)' ;
2015-08-17 17:00:26 -07:00
2015-10-08 11:40:12 -07:00
if ( window . matchMedia ( mq ) . matches ) {
2015-08-17 17:00:26 -07:00
return ;
}
$ ( context ) . find ( '[data-vertical-tabs-panes]' ) . once ( 'vertical-tabs' ) . each ( function ( ) {
var $this = $ ( this ) . addClass ( 'vertical-tabs__panes' ) ;
var focusID = $this . find ( ':hidden.vertical-tabs__active-tab' ) . val ( ) ;
var tab _focus ;
// Check if there are some details that can be converted to
// vertical-tabs.
var $details = $this . find ( '> details' ) ;
if ( $details . length === 0 ) {
return ;
}
// Create the tab column.
var tab _list = $ ( '<ul class="vertical-tabs__menu"></ul>' ) ;
$this . wrap ( '<div class="vertical-tabs clearfix"></div>' ) . before ( tab _list ) ;
// Transform each details into a tab.
$details . each ( function ( ) {
var $that = $ ( this ) ;
var vertical _tab = new Drupal . verticalTab ( {
title : $that . find ( '> summary' ) . text ( ) ,
details : $that
} ) ;
tab _list . append ( vertical _tab . item ) ;
$that
. removeClass ( 'collapsed' )
// prop() can't be used on browsers not supporting details element,
// the style won't apply to them if prop() is used.
. attr ( 'open' , true )
. addClass ( 'vertical-tabs__pane' )
. data ( 'verticalTab' , vertical _tab ) ;
if ( this . id === focusID ) {
tab _focus = $that ;
}
} ) ;
$ ( tab _list ) . find ( '> li' ) . eq ( 0 ) . addClass ( 'first' ) ;
$ ( tab _list ) . find ( '> li' ) . eq ( - 1 ) . addClass ( 'last' ) ;
if ( ! tab _focus ) {
// If the current URL has a fragment and one of the tabs contains an
// element that matches the URL fragment, activate that tab.
var $locationHash = $this . find ( window . location . hash ) ;
if ( window . location . hash && $locationHash . length ) {
tab _focus = $locationHash . closest ( '.vertical-tabs__pane' ) ;
}
else {
tab _focus = $this . find ( '> .vertical-tabs__pane' ) . eq ( 0 ) ;
}
}
if ( tab _focus . length ) {
tab _focus . data ( 'verticalTab' ) . focus ( ) ;
}
} ) ;
}
} ;
/ * *
* The vertical tab object represents a single tab within a tab group .
*
* @ constructor
*
* @ param { object } settings
* @ param { string } settings . title
* The name of the tab .
* @ param { jQuery } settings . details
* The jQuery object of the details element that is the tab pane .
*
* @ fires event : summaryUpdated
*
* @ listens event : summaryUpdated
* /
Drupal . verticalTab = function ( settings ) {
var self = this ;
$ . extend ( this , settings , Drupal . theme ( 'verticalTab' , settings ) ) ;
this . link . attr ( 'href' , '#' + settings . details . attr ( 'id' ) ) ;
this . link . on ( 'click' , function ( e ) {
e . preventDefault ( ) ;
self . focus ( ) ;
} ) ;
// Keyboard events added:
// Pressing the Enter key will open the tab pane.
this . link . on ( 'keydown' , function ( event ) {
if ( event . keyCode === 13 ) {
2015-10-08 11:40:12 -07:00
event . preventDefault ( ) ;
2015-08-17 17:00:26 -07:00
self . focus ( ) ;
// Set focus on the first input field of the visible details/tab pane.
2015-10-21 21:44:50 -07:00
$ ( '.vertical-tabs__pane :input:visible:enabled' ) . eq ( 0 ) . trigger ( 'focus' ) ;
2015-08-17 17:00:26 -07:00
}
} ) ;
this . details
. on ( 'summaryUpdated' , function ( ) {
self . updateSummary ( ) ;
} )
. trigger ( 'summaryUpdated' ) ;
} ;
Drupal . verticalTab . prototype = {
/ * *
* Displays the tab ' s content pane .
* /
focus : function ( ) {
this . details
. siblings ( '.vertical-tabs__pane' )
. each ( function ( ) {
var tab = $ ( this ) . data ( 'verticalTab' ) ;
tab . details . hide ( ) ;
tab . item . removeClass ( 'is-selected' ) ;
} )
. end ( )
. show ( )
. siblings ( ':hidden.vertical-tabs__active-tab' )
. val ( this . details . attr ( 'id' ) ) ;
this . item . addClass ( 'is-selected' ) ;
// Mark the active tab for screen readers.
$ ( '#active-vertical-tab' ) . remove ( ) ;
this . link . append ( '<span id="active-vertical-tab" class="visually-hidden">' + Drupal . t ( '(active tab)' ) + '</span>' ) ;
} ,
/ * *
* Updates the tab ' s summary .
* /
updateSummary : function ( ) {
this . summary . html ( this . details . drupalGetSummary ( ) ) ;
} ,
/ * *
* Shows a vertical tab pane .
*
* @ return { Drupal . verticalTab }
* /
tabShow : function ( ) {
// Display the tab.
this . item . show ( ) ;
// Show the vertical tabs.
this . item . closest ( '.js-form-type-vertical-tabs' ) . show ( ) ;
// Update .first marker for items. We need recurse from parent to retain
// the actual DOM element order as jQuery implements sortOrder, but not
// as public method.
this . item . parent ( ) . children ( '.vertical-tabs__menu-item' ) . removeClass ( 'first' )
. filter ( ':visible' ) . eq ( 0 ) . addClass ( 'first' ) ;
// Display the details element.
this . details . removeClass ( 'vertical-tab--hidden' ) . show ( ) ;
// Focus this tab.
this . focus ( ) ;
return this ;
} ,
/ * *
* Hides a vertical tab pane .
*
* @ return { Drupal . verticalTab }
* /
tabHide : function ( ) {
// Hide this tab.
this . item . hide ( ) ;
// Update .first marker for items. We need recurse from parent to retain
// the actual DOM element order as jQuery implements sortOrder, but not
// as public method.
this . item . parent ( ) . children ( '.vertical-tabs__menu-item' ) . removeClass ( 'first' )
. filter ( ':visible' ) . eq ( 0 ) . addClass ( 'first' ) ;
// Hide the details element.
this . details . addClass ( 'vertical-tab--hidden' ) . hide ( ) ;
// Focus the first visible tab (if there is one).
var $firstTab = this . details . siblings ( '.vertical-tabs__pane:not(.vertical-tab--hidden)' ) . eq ( 0 ) ;
if ( $firstTab . length ) {
$firstTab . data ( 'verticalTab' ) . focus ( ) ;
}
// Hide the vertical tabs (if no tabs remain).
else {
this . item . closest ( '.js-form-type-vertical-tabs' ) . hide ( ) ;
}
return this ;
}
} ;
/ * *
* Theme function for a vertical tab .
*
* @ param { object } settings
* An object with the following keys :
* @ param { string } settings . title
* The name of the tab .
*
* @ return { object }
* This function has to return an object with at least these keys :
* - item : The root tab jQuery element
* - link : The anchor tag that acts as the clickable area of the tab
* ( jQuery version )
* - summary : The jQuery element that contains the tab summary
* /
Drupal . theme . verticalTab = function ( settings ) {
var tab = { } ;
tab . item = $ ( '<li class="vertical-tabs__menu-item" tabindex="-1"></li>' )
. append ( tab . link = $ ( '<a href="#"></a>' )
. append ( tab . title = $ ( '<strong class="vertical-tabs__menu-item-title"></strong>' ) . text ( settings . title ) )
. append ( tab . summary = $ ( '<span class="vertical-tabs__menu-item-summary"></span>' )
)
) ;
return tab ;
} ;
} ) ( jQuery ) ;