123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- (function ($) {
- 'use strict';
- Drupal.FieldGroup = Drupal.FieldGroup || {};
- Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
- /**
- * This script transforms a set of fieldsets into a stack of horizontal
- * tabs. Another tab pane can be selected by clicking on the respective
- * tab.
- *
- * Each tab may have a summary which can be updated by another
- * script. For that to work, each fieldset has an associated
- * 'horizontalTabCallback' (with jQuery.data() attached to the fieldset),
- * which is called every time the user performs an update to a form
- * element inside the tab pane.
- */
- Drupal.behaviors.horizontalTabs = {
- attach: function (context) {
- var width = drupalSettings.widthBreakpoint || 640;
- var mq = '(max-width: ' + width + 'px)';
- if (window.matchMedia(mq).matches) {
- return;
- }
- $(context).find('[data-horizontal-tabs-panes]').once('horizontal-tabs').each(function () {
- var $this = $(this).addClass('horizontal-tabs-panes');
- var focusID = $(':hidden.horizontal-tabs-active-tab', this).val();
- var tab_focus;
- // Check if there are some details that can be converted to horizontal-tabs
- var $details = $this.find('> details');
- if ($details.length === 0) {
- return;
- }
- // If collapse.js did not do his work yet, call it directly.
- if (!$($details[0]).hasClass('.collapse-processed')) {
- Drupal.behaviors.collapse.attach(context);
- }
- // Create the tab column.
- var tab_list = $('<ul class="horizontal-tabs-list"></ul>');
- $(this).wrap('<div class="horizontal-tabs clearfix"></div>').before(tab_list);
- // Transform each details into a tab.
- $details.each(function (i) {
- var $this = $(this);
- var summaryElement = $this.find('> summary .details-title');
- if (!summaryElement.length) {
- summaryElement = $this.find('> summary');
- }
- var summary = summaryElement.clone().children().remove().end().text();
- var horizontal_tab = new Drupal.horizontalTab({
- title: $.trim(summary),
- details: $this
- });
- horizontal_tab.item.addClass('horizontal-tab-button-' + i);
- tab_list.append(horizontal_tab.item);
- $this
- .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('horizontal-tabs-pane')
- .data('horizontalTab', horizontal_tab);
- if (this.id === focusID) {
- tab_focus = $this;
- }
- });
- $(tab_list).find('> li:first').addClass('first');
- $(tab_list).find('> li:last').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 hash = window.location.hash.replace(/[=%;,\/]/g, '');
- if (hash !== '#' && $(hash, this).length) {
- tab_focus = $(window.location.hash, this).closest('.horizontal-tabs-pane');
- }
- else {
- tab_focus = $this.find('> .horizontal-tabs-pane:first');
- }
- }
- if (tab_focus.length) {
- tab_focus.data('horizontalTab').focus();
- }
- });
- }
- };
- /**
- * The horizontal tab object represents a single tab within a tab group.
- *
- * @param {object} settings
- * An object with the following keys:
- * - title: The name of the tab.
- * - details: The jQuery object of the details element that is the tab pane.
- */
- Drupal.horizontalTab = function (settings) {
- var self = this;
- $.extend(this, settings, Drupal.theme('horizontalTab', 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) {
- event.preventDefault();
- if (event.keyCode === 13) {
- self.focus();
- // Set focus on the first input field of the visible details/tab pane.
- $('.horizontal-tabs-pane :input:visible:enabled:first').trigger('focus');
- }
- });
- // Only bind update summary on forms.
- if (this.details.drupalGetSummary) {
- this.details
- .on('summaryUpdated', function () {
- self.updateSummary();
- })
- .trigger('summaryUpdated');
- }
- };
- Drupal.horizontalTab.prototype = {
- /**
- * Displays the tab's content pane.
- */
- focus: function () {
- this.details
- .removeClass('horizontal-tab-hidden')
- .siblings('.horizontal-tabs-pane')
- .each(function () {
- var tab = $(this).data('horizontalTab');
- tab.details.addClass('horizontal-tab-hidden');
- tab.item.removeClass('selected');
- })
- .end()
- .siblings(':hidden.horizontal-tabs-active-tab')
- .val(this.details.attr('id'));
- this.item.addClass('selected');
- // Mark the active tab for screen readers.
- $('#active-horizontal-tab').remove();
- this.link.append('<span id="active-horizontal-tab" class="visually-hidden">' + Drupal.t('(active tab)') + '</span>');
- },
- /**
- * Updates the tab's summary.
- */
- updateSummary: function () {
- this.summary.html(this.details.drupalGetSummary());
- },
- /**
- * Shows a horizontal tab pane.
- *
- * @return {Drupal.horizontalTab} The current horizontal tab.
- */
- tabShow: function () {
- // Display the tab.
- this.item.removeClass('horizontal-tab-hidden');
- // 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('.horizontal-tab-button').removeClass('first')
- .filter(':visible:first').addClass('first');
- // Display the details element.
- this.details.removeClass('horizontal-tab-hidden');
- // Focus this tab.
- this.focus();
- return this;
- },
- /**
- * Hides a horizontal tab pane.
- *
- * @return {Drupal.horizontalTab} The current horizontal tab.
- */
- tabHide: function () {
- // Hide this tab.
- this.item.addClass('horizontal-tab-hidden');
- // 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('.horizontal-tab-button').removeClass('first')
- .filter(':visible:first').addClass('first');
- // Hide the details element.
- this.details.addClass('horizontal-tab-hidden');
- // Focus the first visible tab (if there is one).
- var $firstTab = this.details.siblings('.horizontal-tabs-pane:not(.horizontal-tab-hidden):first');
- if ($firstTab.length) {
- $firstTab.data('horizontalTab').focus();
- }
- else {
- // Hide the vertical tabs (if no tabs remain).
- this.item.closest('.form-type-horizontal-tabs').hide();
- }
- return this;
- }
- };
- /**
- * Theme function for a horizontal tab.
- *
- * @param {object} settings
- * An object with the following keys:
- * - 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.horizontalTab = function (settings) {
- var tab = {};
- var idAttr = settings.details.attr('id');
- tab.item = $('<li class="horizontal-tab-button" tabindex="-1"></li>')
- .append(tab.link = $('<a href="#' + idAttr + '"></a>')
- .append(tab.title = $('<strong></strong>').text(settings.title))
- );
- // No need to add summary on frontend.
- if (settings.details.drupalGetSummary) {
- tab.link.append(tab.summary = $('<span class="summary"></span>'));
- }
- return tab;
- };
- })(jQuery, Modernizr);
|