diff --git a/core/includes/common.inc b/core/includes/common.inc index c9913a2..82b8228 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -6539,6 +6539,9 @@ function drupal_common_theme() { 'links' => array( 'variables' => array('links' => array(), 'attributes' => array('class' => array('links')), 'heading' => array()), ), + 'dropbutton' => array( + 'variables' => array('title' => NULL, 'links' => NULL, 'attributes' => array()), + ), 'image' => array( // HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft // allows the alt attribute to be omitted in some cases. Therefore, diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 2d1d375..15ffcd9 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1691,6 +1691,99 @@ function theme_links($variables) { } /** + * Builds a render array for theme_dropbutton(). + */ +function template_preprocess_dropbutton(&$variables) { + // Remove the 'inline' and 'links' classes. + if (!empty($variables['attributes']['class'])) { + foreach ($variables['attributes']['class'] as $key => $class) { + if ($class === 'inline' || $class === 'links') { + unset($variables['attributes']['class'][$key]); + } + } + } + + // Give each button a unique ID. + static $id = 0; + + $variables['element'] = array( + '#type' => 'container', + '#attached' => array( + 'library' => array( + array('system', 'drupal.dropbutton'), + ), + ), + '#attributes' => array( + 'class' => array( + 'dropbutton-no-js', + 'dropbutton', + ), + 'id' => 'dropbutton-' . ++$id, + ), + ); + if (count($variables['links']) > 1) { + $title = isset($variables['title']) ? check_plain($variables['title']) : t('Open dropbutton'); + $variables['element']['#attributes']['class'][] = 'dropbutton-multiple'; + $variables['element']['wrapper'] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'dropbutton-link', + ), + ), + 'link' => array( + '#type' => 'link', + '#href' => '', + '#title' => '' . $title . '', + '#options' => array( + 'fragment' => 'noname', + 'html' => TRUE, + ), + '#attributes' => array( + 'class' => array( + 'dropbutton-arrow', + ), + ), + ), + ); + } + $variables['element']['content'] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'dropbutton-content', + ), + ), + 'links' => array( + '#theme' => 'links', + '#links' => $variables['links'], + '#attributes' => $variables['attributes'], + ), + ); +} + +/** + * Create a dropbutton menu. + * + * @param $title + * The text to place in the clickable area to activate the dropbutton. This + * text is indented to -9999px by default. + * @param $links + * A list of links to provide within the dropbutton, suitable for use + * in via Drupal's theme('links'). + * @param $image + * If true, the dropbutton link is an image and will not get extra decorations + * that a text dropbutton link will. + * @param $class + * An optional class to add to the dropbutton's container div to allow you + * to style a single dropbutton however you like without interfering with + * other dropbuttons. + */ +function theme_dropbutton($variables) { + return render($variables['element']); +} + +/** * Returns HTML for an image. * * @param $variables diff --git a/core/misc/dropbutton.base.css b/core/misc/dropbutton.base.css new file mode 100644 index 0000000..a73e02c --- /dev/null +++ b/core/misc/dropbutton.base.css @@ -0,0 +1,68 @@ +.dropbutton .dropbutton-content ul { + list-style-image: none; + list-style-type: none; + margin: 0; + padding: 0; +} +.dropbutton .dropbutton-content ul li { + line-height: 1.333; +} +.dropbutton .dropbutton-content ul.actions { + list-style-type: none; + margin: 0; + padding: 0; +} + +/** + * This creates the dropbutton arrow and inherits the link color. + */ +.dropbutton-link { + bottom: 0; + overflow: hidden; + position: absolute; + right: 0; + text-indent: 110%; + top: 0; + white-space: nowrap; + width: 17px; +} +.dropbutton-arrow { + border-bottom-color: transparent; + border-left-color: transparent; + border-right-color: transparent; + border-style: solid; + border-width: 4px 4px 0; + display: none; + line-height: 0; + position: absolute; + right: 6px; + top: 0.667em; +} + +.js .dropbutton-arrow { + display: inline-block; +} +.js .dropbutton { + display: inline-block; +} +.js .dropbutton-multiple { + padding-right: 18px; + position: relative; +} +.js .dropbutton-multiple .dropbutton-content ul { + margin: 0; + overflow: hidden; + padding: 0; +} +.js .dropbutton-multiple .dropbutton-content li, +.js .dropbutton-multiple .dropbutton-content a { + display: block; +} +.js .dropbutton-multiple.open { + z-index: 100; +} +.js .dropbutton-multiple.open .dropbutton-arrow { + border-bottom: 4px solid; + border-top-color: transparent; + top: 0.333em; +} diff --git a/core/misc/dropbutton.js b/core/misc/dropbutton.js new file mode 100644 index 0000000..fbc0f30 --- /dev/null +++ b/core/misc/dropbutton.js @@ -0,0 +1,99 @@ +/** + * @file + * Implement a simple, clickable dropbutton menu. + * + * See dropbutton.theme.inc for primary documentation. + * + * The javascript relies on four classes: + * - The dropbutton must be fully contained in a div with the class + * dropbutton. It must also contain the class dropbutton-no-js + * which will be immediately removed by the javascript; this allows for + * graceful degradation. + * - The trigger that opens the dropbutton must be an a tag wit hthe class + * dropbutton-link. The href should just be '#' as this will never + * be allowed to complete. + * - The part of the dropbutton that will appear when the link is clicked must + * be a div with class dropbutton-container. + * - Finally, dropbutton-hover will be placed on any link that is being + * hovered over, so that the browser can restyle the links. + * + * This tool isn't meant to replace click-tips or anything, it is specifically + * meant to work well presenting menus. + */ + +(function ($) { + +"use strict"; + +Drupal.behaviors.dropbutton = { + attach: function (context) { + var $context = $(context); + // Process buttons. All dropbuttons are buttons. + $context.find('.dropbutton') + .once('dropbutton') + .removeClass('dropbutton-no-js'); + + // Process dropbuttons. Not all buttons are dropbuttons. + $context.find('.dropbutton-multiple').once('dropbutton-multiple', function () { + var $dropbutton = $(this); + var $button = $dropbutton.find('.dropbutton-content'); + var $secondaryActions = $button.find('li').not(':first'); + var $arrow = $dropbutton.find(".dropbutton-link"); + var open = false; + var hovering = false; + var timerID = 0; + + var toggle = function (close) { + // if it's open or we're told to close it, close it. + if (open || close) { + // If we're just toggling it, close it immediately. + if (!close) { + open = false; + $secondaryActions.slideUp(100); + $dropbutton.removeClass('open'); + } + else { + // If we were told to close it, wait half a second to make + // sure that's what the user wanted. + // Clear any previous timer we were using. + if (timerID) { + clearTimeout(timerID); + } + timerID = setTimeout(function () { + if (!hovering) { + open = false; + $secondaryActions.slideUp(100); + $dropbutton.removeClass('open'); + }}, 500); + } + } + else { + // open it. + open = true; + $secondaryActions.animate({height: "show", opacity: "show"}, 100); + $dropbutton.addClass('open'); + } + }; + // Hide the secondary actions initially. + $secondaryActions.hide(); + + $arrow.click(function () { + toggle(); + return false; + }); + + $dropbutton.hover( + function () { + hovering = true; + }, // hover in + function () { // hover out + hovering = false; + toggle(true); + return false; + } + ); + }); + } +}; + +})(jQuery); diff --git a/core/misc/dropbutton.theme.css b/core/misc/dropbutton.theme.css new file mode 100644 index 0000000..ba8f95d --- /dev/null +++ b/core/misc/dropbutton.theme.css @@ -0,0 +1,77 @@ +/** + * Make dropbuttons pretty. + */ +.dropbutton-content { + padding-bottom: 0; + padding-top: 0; +} + +.dropbutton-arrow:focus { + outline: medium none; +} + +.js .dropbutton { + background-image: -moz-linear-gradient(-90deg, white 0, #f9f9f9 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(1, #f9f9f9)); + background-image: -webkit-linear-gradient(-90deg, white 0, #f9f9f9 100%); + background-image: linear-gradient(-90deg, #ffffff 0%, #f9f9f9 100%); + border: #cccccc solid 1px; + border-radius: 5px; + cursor: pointer; + font-size: 11px; + padding-bottom: 1px; + padding-top: 1px; +} +.js .dropbutton:hover { + background-image: -moz-linear-gradient(-90deg, white 0, #f1f1f1 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(1, #f1f1f1)); + background-image: -webkit-linear-gradient(-90deg, white 0, #f1f1f1 100%); + background-image: linear-gradient(-90deg, #ffffff 0%, #f1f1f1 100%); + border-color: #b8b8b8; +} +.js .dropbutton:active { + border-color: #a0a0a0; +} +.js .dropbutton li a { + padding-left: 9px; + padding-right: 9px; +} +.js .dropbutton .dropbutton-content a { + background-image: none; + border: medium none; +} +.js .dropbutton-multiple { + background-color: inherit; + position: absolute; +} +.js .dropbutton-multiple .dropbutton-content { + border-right: 1px solid #e8e8e8; +} +.js .dropbutton-multiple.open { + -moz-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); + -webkit-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); + box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25); +} +.js .dropbutton-multiple.open:hover { + background-image: -moz-linear-gradient(-90deg, white 0, #f9f9f9 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(1, #f9f9f9)); + background-image: -webkit-linear-gradient(-90deg, white 0, #f9f9f9 100%); + background-image: linear-gradient(-90deg, #ffffff 0%, #f9f9f9 100%); + border-color: #d0d0d0; +} +.js .dropbutton-multiple.open .dropbutton-link { + border-left: 1px solid white; +} +.js .dropbutton-multiple.open .dropbutton-content { + padding-bottom: 4px; +} +.js .dropbutton-multiple.open li a, +.js .dropbutton-multiple.open li input { + padding-right: 9px; +} +.js .dropbutton-multiple.open li + li { + border-top: 1px solid #efefef; + margin-top: 4px; + padding-bottom: 0; + padding-top: 4px; +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 922b8a7..0fc0727 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1392,6 +1392,25 @@ function system_library_info() { ), ); + // Dropbutton. + $libraries['drupal.dropbutton'] = array( + 'title' => 'Dropbutton', + 'website' => 'http://drupal.org/node/1608878', + 'version' => '1.0', + 'js' => array( + 'core/misc/dropbutton.js' => array(), + ), + 'css' => array( + 'core/misc/dropbutton.base.css' => array(), + 'core/misc/dropbutton.theme.css' => array(), + ), + 'dependencies' => array( + array('system', 'jquery'), + array('system', 'jquery.once'), + array('system', 'drupal'), + ), + ); + // Vertical Tabs. $libraries['drupal.vertical-tabs'] = array( 'title' => 'Vertical Tabs',