core/modules/contextual/contextual.js | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/modules/contextual/contextual.js b/core/modules/contextual/contextual.js index 1cdeb2e..b7c8cb5 100644 --- a/core/modules/contextual/contextual.js +++ b/core/modules/contextual/contextual.js @@ -14,17 +14,55 @@ var contextuals = []; */ Drupal.behaviors.contextual = { attach: function (context) { + var that = this; $('ul.contextual-links', context).once('contextual', function () { var $this = $(this); var contextual = new Drupal.contextual($this, $this.closest('.contextual-region')); contextuals.push(contextual); $this.data('drupal-contextual', contextual); + that._adjustIfNestedAndOverlapping(this); }); // Bind to edit mode changes. $('body').once('contextual', function () { $(document).on('drupalEditModeChanged.contextual', toggleEditMode); }); + }, + + /** + * Determines if a contextual link is nested & overlapping, if so: adjusts it. + * + * This only deals with two levels of nesting; deeper levels are not touched. + * + * @param contextualLink + * A contextual link DOM element. + */ + _adjustIfNestedAndOverlapping: function (contextualLink) { + var $contextuals = jQuery(contextualLink) + .parents('.contextual-region:last') + .find('.contextual'); + + // Early-return when there's no nesting. + if ($contextuals.length === 1) { + return; + } + + // If the two contextual links overlap, then we move the second one. + var firstTop = $contextuals.eq(0).offset().top; + var secondTop = $contextuals.eq(1).offset().top; + if (firstTop === secondTop) { + var $nestedContextual = $contextuals.eq(1); + + // Retrieve height of nested contextual link. + var height = 0; + var $trigger = $nestedContextual.find('.trigger'); + $trigger.removeClass('element-invisible'); + height = $nestedContextual.height(); + $trigger.addClass('element-invisible'); + + // Adjust nested contextual link's position. + $nestedContextual.css({ top: $nestedContextual.position().top + height }); + } } };