diff --git a/paragraphs.collapsible.js b/paragraphs.collapsible.js index 71e2a01..732c1c5 100644 --- a/paragraphs.collapsible.js +++ b/paragraphs.collapsible.js @@ -1,66 +1,120 @@ /** * @file - * Provides JavaScript for Paragraphs. + * Provides JavaScript for Collapsible Paragraphs. */ -(function ($, Drupal) { - - // Collapse effects. - var toggleEffects = { - expand : function(elements) { - elements.slideDown(); - }, - collapse : function(elements) { - elements.slideUp(); - } - }; +(function ($, Drupal, window) { + // Stored variables. + var mToolbarHeight = null; /** * Show / Hide a single paragraph. + * + * @param object item + * The element of the field item row. + * @param string action + * The toggle action - 'collapse' or 'expand'. + * @param object options + * A set of options to configure the toggle: + * - excludeNew (bool): TRUE to exclude operating on new AJAX content. + * - excludeErrors (bool): TRUE to exclude operating on items with errors. + * - fx (object): Options for the slide effects. + * + * @return boolean + * TRUE if the paragraph was toggled, */ - var toggleParagraph = function(item, action) { + var toggleParagraph = function(item, action, options) { var $item = $(item); var elements; + var settings = $.extend(true, + { + 'excludeNew': false, + 'excludeErrors': false, + 'fx': { + duration: 'fast', + easing: 'linear' + } + }, + options + ); + // Except the first div, show/hide others. - var $ajax_content = $item.children('div.ajax-new-content'); + var $ajax_content = $item.children('.ajax-new-content'); if ($ajax_content.length > 0) { - elements = $ajax_content.children('div:not(.paragraph-bundle-title, .paragraph-bundle-preview), fieldset'); + // Exit if excluding new content. + if (settings.excludeNew) { + return false; + } + elements = $ajax_content.children(':not(.paragraph-bundle-title, .paragraph-bundle-preview), fieldset'); } else { - elements = $item.children('div:not(.paragraph-bundle-title, .paragraph-bundle-preview), fieldset'); + elements = $item.children(':not(.paragraph-bundle-title, .paragraph-bundle-preview), fieldset'); + } + + // Do not collapse deleted paragraphs. + if (action === 'collapse' && elements.filter('.form-actions').find('[name$="deleteconfirm_button"]').length > 0) { + return false; } - // Invoke collapse action. - toggleEffects[action](elements); + // Exclude errors. + if (settings.excludeErrors && elements.find('.error').length > 0) { + return false; + } - // Add class to item row. - if (action == 'collapse') { + // Invoke collapse actions. + if (action === 'collapse') { + elements.slideUp(settings.fx); $item.removeClass('expanded').addClass('collapsed'); } else { + elements.slideDown(settings.fx); $item.removeClass('collapsed').addClass('expanded'); } + + return true; }; /** * Show / Hide all paragraphs. + * + * @param object triggerElement + * The trigger element to toggle all child paragraphs. + * @param string action + * The toggle action - 'collapse' or 'expand'. + * @param object options + * See toggleParagraph(). + * + * @return int + * The count of paragraphs toggled. */ - var toggleParagraphs = function(element, action) { - // Fetch all paragraph bundles. - var $form_item = $(element).closest('.form-item'); + var toggleParagraphs = function(triggerElement, action, options) { + var toggledCount = 0; + var $form_item = $(triggerElement).closest('.form-item'); if ($form_item.length > 0) { - var paragraph_bundles = $form_item.children('table.field-multiple-table').find('td.paragraph-bundle-content-collapsible'); + var paragraph_bundles = $form_item.children('.field-multiple-table').find('.paragraph-bundle-content-collapsible'); paragraph_bundles.each(function () { - toggleParagraph(this, action); + if (toggleParagraph(this, action, options)) { + toggledCount++; + } }); } + + return toggledCount; }; /** * Trigger show / hide for given trigger element. + * + * @param object triggerElement + * The trigger element to toggle all child paragraphs. + * @param object options + * See toggleParagraph(). + * + * @return int + * The count of paragraphs toggled. */ - var toggleAll = function(triggerElement) { + var toggleAll = function(triggerElement, options) { var $t = $(triggerElement); // Build triggers. @@ -71,33 +125,49 @@ if ($parent_table.length > 0) { if ($parent_table.hasClass('field-multiple-table')) { // Items table click, add sticky header trigger. - $triggers.push($parent_table.siblings('table.sticky-header').find('span.paragraphs-collapsible a')); + $triggers.push($parent_table.siblings('.sticky-header').find('.paragraphs-collapsible-trigger-link')); } else if ($parent_table.hasClass('sticky-header')) { // Sticky header click, add items table click. - $triggers.push($parent_table.siblings('table.field-multiple-table').find('span.paragraphs-collapsible a')); + $triggers.push($parent_table.siblings('table.field-multiple-table').find('.paragraphs-collapsible-trigger-link')); } } - if ($t.hasClass('collapsed')) { - // Expand. - toggleParagraphs(triggerElement, 'expand'); + var action = $t.hasClass('collapsed') ? 'expand' : 'collapse'; + var toggledCount = toggleParagraphs(triggerElement, action, options); + if (toggledCount > 0) { $.each($triggers, function(index, $trigger) { if ($trigger.length > 0) { - $trigger.removeClass('collapsed').addClass('expanded'); - $trigger.text(Drupal.t('Collapse All')); + if (action === 'expand') { + $trigger.removeClass('collapsed').addClass('expanded'); + $trigger.text(Drupal.t('Collapse All')); + } + else { + $trigger.removeClass('expanded').addClass('collapsed'); + $trigger.text(Drupal.t('Expand All')); + } } }); } - else { - // Collapse. - toggleParagraphs(triggerElement, 'collapse'); - $.each($triggers, function(index, $trigger) { - if ($trigger.length) { - $trigger.removeClass('expanded').addClass('collapsed'); - $trigger.text(Drupal.t('Expand All')); - } - }); + + return toggledCount; + }; + + /** + * Scroll a given fieldset into view as much as possible. + */ + var collapseScrollIntoView = function(element) { + var $element = $(element).first(); + var offset = $element.offset(); + var slack = 55; + + if (offset.top > 0) { + if (mToolbarHeight === null) { + mToolbarHeight = $('#toolbar').outerHeight() || 0; + } + var y = Math.floor(offset.top - mToolbarHeight - slack); + y = y > 0 ? y : 0; + $(window).scrollTop(y); } }; @@ -108,38 +178,50 @@ Drupal.behaviors.paragraphsCollapsible = { attach: function (context, settings) { // Trigger for single paragaph. - $('td.paragraph-bundle-content-collapsible > .paragraph-bundle-title', context).once("paragraphs").click(function(event) { - // Prevent the default click event. - event.preventDefault(); - var $row = $(this).parent('td.paragraph-bundle-content-collapsible'); - if ($row.length) { - var action = $row.hasClass('collapsed') ? 'expand' : 'collapse'; - toggleParagraph($row, action); - } - }); + $('.paragraph-bundle-content-collapsible > .paragraph-bundle-title, .paragraph-bundle-content-collapsible > .ajax-new-content > .paragraph-bundle-title', context) + .once("paragraphs").click(function(event) { + // Prevent the default click event. + event.preventDefault(); + var $row = $(this).closest('.paragraph-bundle-content-collapsible'); + if ($row.length) { + var action = $row.hasClass('collapsed') ? 'expand' : 'collapse'; + toggleParagraph($row, action); + } + }); // Trigger for all paragaphs. - $('span.paragraphs-collapsible a', context).once("paragraphs") + $('.paragraphs-collapsible-trigger-link', context).once("paragraphs") .click(function(event) { // Prevent the default click event. event.preventDefault(); toggleAll(this); }) - .each(function() { - // Initial collapse processing. - var $t = $(this); - - // If trigger is flagged as expanded - // AND parent is set to collapse initially - // AND is not in the sticky header. - if ($t.hasClass('expanded')) { - if ($t.parent('.paragraphs-collapsible-init-collapsed').length > 0 && - $t.closest('table').filter('.field-multiple-table').length > 0) { - toggleAll($t); + .filter('.paragraphs-collapsible-init-collapsed') + .filter(':not(.collapsed)').each(function() { + // Initial collapse processing. + var $t = $(this); + + // If parent table is field item table - not the sticky header table + // or a parent field item's table when using nested paragraphs. + var $table = $t.closest('table').filter('.field-multiple-table') + if ($table.length > 0) { + var toggleOptions = {'excludeNew': true, 'excludeErrors': true}; + + // Scroll to new content after other paragraphs get collapsed. + var $newContent = $('.paragraph-bundle-content > .ajax-new-content', $table) + if ($newContent.length > 0) { + toggleOptions.fx = { + complete: function() { + collapseScrollIntoView($newContent); + } + } + } + + // Collapse all paragraphs. + toggleAll($t, toggleOptions); } - } - }); + }); } }; -})(jQuery, Drupal); +})(jQuery, Drupal, this); diff --git a/paragraphs.module b/paragraphs.module index 54f3c87..de86f46 100644 --- a/paragraphs.module +++ b/paragraphs.module @@ -1216,20 +1216,25 @@ function theme_paragraphs_field_multiple_value_form($variables) { // Open collapsible processing. $open_collapsible_enabled = FALSE; - $expand_collapse = ''; + $expand_collapse_markup = ''; if ($default_edit_mode == 'open' && !empty($instance['settings']['open_collapsible'])) { $open_collapsible_enabled = TRUE; - $expand_collapse_classes = 'paragraphs-collapsible'; + $expand_collapse_link_classes = array( + 'paragraphs-collapsible-trigger-link', + 'expanded', + ); if (!empty($instance['settings']['open_collapsible_collapsed'])) { - $expand_collapse_classes .= ' paragraphs-collapsible-init-collapsed'; + $expand_collapse_link_classes[] = 'paragraphs-collapsible-init-collapsed'; } - $expand_collapse = ''; - $expand_collapse .= l(t('Collapse All'), '#', array( + $expand_collapse_markup = ''; + $expand_collapse_markup .= l(t('Collapse All'), '#', array( 'external' => TRUE, - 'attributes' => array('class' => array('expanded')), + 'attributes' => array( + 'class' => $expand_collapse_link_classes, + ), )); - $expand_collapse .= ''; + $expand_collapse_markup .= ''; } // Sort items according to '_weight' (needed when the form comes back after @@ -1257,7 +1262,7 @@ function theme_paragraphs_field_multiple_value_form($variables) { if ($open_collapsible_enabled) { $header_text = t('!title !required !collapsible', array( - '!collapsible' => $expand_collapse, + '!collapsible' => $expand_collapse_markup, '!title' => $element['#title'], '!required' => $required, ));