diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module index a704221..584cace 100644 --- a/core/modules/edit/edit.module +++ b/core/modules/edit/edit.module @@ -47,26 +47,29 @@ function edit_toolbar() { } $tab['edit'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => t('Edit'), - 'href' => '', - 'html' => FALSE, - 'attributes' => array( - 'class' => array('icon', 'icon-edit', 'edit-nothing-editable-hidden'), + '#type' => 'link', + '#title' => t('Edit'), + '#href' => '', + '#options' => array( + 'html' => FALSE, + 'attributes' => array( + 'id' => 'toolbar-tab-edit', + 'class' => array('icon', 'icon-edit', 'edit-nothing-editable-hidden'), + ), ), ), - 'tray' => array( - '#attached' => array( - 'library' => array( - array('edit', 'edit'), - ), + '#attached' => array( + 'library' => array( + array('edit', 'edit'), ), ), ); // Include the attachments and settings for all available editors. $attachments = drupal_container()->get('edit.editor.selector')->getAllEditorAttachments(); - $tab['edit']['tray']['#attached'] = NestedArray::mergeDeep($tab['edit']['tray']['#attached'], $attachments); + $tab['edit']['#attached'] = NestedArray::mergeDeep($tab['edit']['#attached'], $attachments); return $tab; } diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module index b9c737d..237456a 100644 --- a/core/modules/shortcut/shortcut.module +++ b/core/modules/shortcut/shortcut.module @@ -608,13 +608,8 @@ function shortcut_preprocess_page(&$variables) { * Implements hook_toolbar(). */ function shortcut_toolbar() { + $items = array(); $links = shortcut_renderable_links(); - $links['#attached'] = array( - 'css' => array( - drupal_get_path('module', 'shortcut') . '/shortcut.base.css', - drupal_get_path('module', 'shortcut') . '/shortcut.theme.css', - ), - ); $shortcut_set = shortcut_current_displayed_set(); $configure_link = NULL; if (shortcut_set_edit_access($shortcut_set)) { @@ -625,26 +620,35 @@ function shortcut_toolbar() { '#options' => array('attributes' => array('class' => array('edit-shortcuts'))), ); } - - $links_tray = array( - '#heading' => t('User-defined shortcuts'), - 'shortcuts' => $links, - 'configure' => $configure_link, - ); - - $items['shortcuts'] = array( - 'tab' => array( - 'title' => t('Shortcuts'), - 'href' => 'admin/config/user-interface/shortcut', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('Shortcuts'), - 'class' => array('icon', 'icon-shortcut'), + if (!empty($links) || !empty($configure_link)) { + $items['shortcuts'] = array( + '#type' => 'toolbar_item', + 'tab' => array( + '#type' => 'link', + '#title' => t('Shortcuts'), + '#href' => 'admin/config/user-interface/shortcut', + '#options' => array( + 'attributes' => array( + 'title' => t('Shortcuts'), + 'class' => array('icon', 'icon-shortcut'), + ), + ), ), - ), - 'tray' => $links_tray, - 'weight' => -10, - ); + 'tray' => array( + '#heading' => t('User-defined shortcuts'), + 'shortcuts' => $links, + 'configure' => $configure_link, + ), + '#weight' => -10, + '#attached' => array( + 'css' => array( + drupal_get_path('module', 'shortcut') . '/shortcut.base.css', + drupal_get_path('module', 'shortcut') . '/shortcut.theme.css', + ), + ), + ); + } + return $items; } diff --git a/core/modules/toolbar/css/toolbar.base.css b/core/modules/toolbar/css/toolbar.base.css index cbdf153..e6cf95a 100644 --- a/core/modules/toolbar/css/toolbar.base.css +++ b/core/modules/toolbar/css/toolbar.base.css @@ -39,12 +39,12 @@ html.js .toolbar { .toolbar .menu li { padding-top: 0; } -.js .toolbar .bar li, +.js .toolbar .bar .tab, .js .toolbar .menu li { display: block; } -.js .toolbar .bar li, -.js .toolbar .horizontal li { +.js .toolbar .bar .tab, +.js .toolbar .horizontal .tab { float: left; /* LTR */ } .js .toolbar a { @@ -62,13 +62,13 @@ html.js .toolbar { width: 100%; } @media only screen { - .js .toolbar .bar li, + .js .toolbar .bar .tab, .js .toolbar .tray li { float: none; /* LTR */ } } @media only screen and (min-width: 16.5em) { - .js .toolbar .bar li, + .js .toolbar .bar .tab, .js .toolbar .horizontal li { float: left; /* LTR */ } diff --git a/core/modules/toolbar/css/toolbar.theme.css b/core/modules/toolbar/css/toolbar.theme.css index 08a573c..c1317e5 100644 --- a/core/modules/toolbar/css/toolbar.theme.css +++ b/core/modules/toolbar/css/toolbar.theme.css @@ -31,19 +31,21 @@ box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3333); color: #dddddd; } -.toolbar .bar a:hover { +.toolbar .bar a { + color: #ffffff; +} +.toolbar .bar .tab > a { + font-weight: bold; +} +.toolbar .bar .tab > a:hover { background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%); background-image: linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%); text-decoration: none; } -.toolbar .bar a.active { +.toolbar .bar .tab > a.active { background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%); background-image: linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%); } -.toolbar .bar a { - color: #ffffff; - font-weight: bold; -} /** * Toolbar tray. diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js index 830684d..45e91ad 100644 --- a/core/modules/toolbar/js/toolbar.js +++ b/core/modules/toolbar/js/toolbar.js @@ -80,7 +80,7 @@ Drupal.behaviors.toolbar = { // query application decide the orientation. changeOrientation((locked) ? 'vertical' : ((mql.wide.matches) ? 'horizontal' : 'vertical'), locked); // Render the main menu as a nested, collapsible accordion. - $toolbar.find('.administration.tray .toolbar-menu > .menu').toolbarMenu(); + $toolbar.find('.toolbar-menu-administration > .menu').toolbarMenu(); // Call setHeight on screen resize. Wrap it in debounce to prevent // setHeight from being called too frequently. var setHeight = Drupal.debounce(Drupal.toolbar.setHeight, 200); @@ -152,7 +152,7 @@ Drupal.toolbar.toggleTray = function (event) { localStorage.removeItem('Drupal.toolbar.activeTab'); } // Disable non-selected tabs and trays. - $toolbar.find('.bar a') + $toolbar.find('.bar .trigger') .not($tab) .removeClass('active') // Set aria-pressed to false. diff --git a/core/modules/toolbar/templates/toolbar.twig b/core/modules/toolbar/templates/toolbar.twig deleted file mode 100644 index c2181df..0000000 --- a/core/modules/toolbar/templates/toolbar.twig +++ /dev/null @@ -1,39 +0,0 @@ -{# -/** - * @file - * Default template for admin toolbar. - * - * Available variables: - * - * - tabs: Themed links for the top level tabs. - * - trays: An array of trays. It contains: - * - content: The themed tray content. - * - attributes: HTML attributes for the surrounding element. It includes: - * - id: The unique id of the tray. This corresponds to the module name - * registered the tray. - * - class: A list of classes to target the trays for styling. - * - attributes: HTML attributes for the surrounding element. It includes: - * - id: The unique id of the toolbar. - * - class: A list of classes to target the toolbar for styling. - * - * @see template_preprocess() - * @see template_preprocess_toolbar() - * - * @ingroup themeable - */ -#} - - diff --git a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module index cadddc9..5bab7a9 100644 --- a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module +++ b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module @@ -6,34 +6,42 @@ */ /** - * Um + * Implements hook_toolbar(). */ function toolbar_test_toolbar() { - $tray_items = array( - l('link 1', ''), - l('link 2', ''), - l('link 3', ''), - ); - $items['testing'] = array( + + $items['testing'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => t('Test tab'), - 'href' => '', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('Test tab'), + '#type' => 'link', + '#title' => t('Test tab'), + '#href' => '', + '#options' => array( + 'html' => FALSE, + 'attributes' => array( + 'id' => 'toolbar-tab-testing', + 'title' => t('Test tab'), + ), ), ), 'tray' => array( - '#heading' => t('Test tray'), + '#wrapper_attributes' => array( + 'id' => 'toolbar-tray-testing', + ), 'content' => array( '#theme' => 'item_list', - '#items' => $tray_items, + '#items' => array( + l(t('link 1'), ''), + l(t('link 2'), ''), + l(t('link 3'), ''), + ), + '#prefix' => '

' . t('Test tray') . '

', '#attributes' => array( 'class' => array('menu'), ), ), ), - 'weight' => 50, + '#weight' => 50, ); return $items; diff --git a/core/modules/toolbar/toolbar.api.php b/core/modules/toolbar/toolbar.api.php index 42ca3fe..3ff0999 100644 --- a/core/modules/toolbar/toolbar.api.php +++ b/core/modules/toolbar/toolbar.api.php @@ -2,7 +2,7 @@ /** * @file - * Hooks provided by the Toolbar module. + * Hooks provided by the toolbar module. */ /** @@ -11,82 +11,144 @@ */ /** - * Add items to the Toolbar menu. + * Add items to the toolbar menu. * - * The Toolbar has two parts. The tabs are menu links, rendered by - * theme_links(), that are displayed if the module is enabled and the user has - * the 'access toolbar' permission. The trays are render elements, usually lists - * of links, and each tray corresponds to a tab. When a tab is activated, the - * corresponding tray is displayed; only one tab can be activated at a time. If - * a tab does not have a corresponding tray, or if javascript is disabled, then - * the tab is simply a link. + * The toolbar is a container for adminstrative and site-global interactive + * components. * - * This hook is invoked in toolbar_view(). + * The toolbar provides a common styling for items denoted by the .tab class. + * The theme wrapper toolbar_tab_wrapper is provided to wrap a toolbar item + * with the appropriate markup to apply the styling. + * + * The toolbar provides a construct called a 'tray'. The tray is a container + * for content. The tray may be associated with a toggle in the adminstration + * bar. The toggle shows or hides the tray and is optimized for small and + * large screens. To create this association, hook_toolbar() returns one or + * more render elements of type 'toolbar_item', containing the toggle and tray + * elements in its 'tab' and 'tray' children. + * + * The following properties are available: + * - '#weight': Optional. Integer weight used for sorting toolbar items in + * adminstration bar area. + * + * This hook is invoked in toolbar_pre_render(). * * @return * An array of toolbar items, keyed by unique identifiers such as 'home' or * 'administration', or the short name of the module implementing the hook. - * The corresponding value is an associative array that may contain the - * following key-value pairs: - * - 'tab': Required, unless the item is adding links to an existing tab. An - * array with keys 'title', 'href', 'html', and 'attributes', as used by - * theme_links(). The 'href' value is ignored unless 'tray' (below) is - * omitted, or if javascript is disabled. - * - 'tray': Optional. A render element that is displayed when the tab is - * activated. - * - 'weight': Optional. Integer weight used for sorting tabs. + * The corresponding value is a render element of type 'toolbar_item'. * - * @see toolbar_view() + * @see toolbar_pre_render() * @ingroup toolbar_tabs */ function hook_toolbar() { $items = array(); - // The 'Home' tab is a simple link, with no corresponding tray. + // Add a search field to the toolbar. The search field employs no toolbar + // module theming functions. + $items['global_search'] = array( + '#type' => 'toolbar_item', + 'tab' => array( + '#type' => 'search', + '#attributes' => array( + 'placeholder' => t('Search the site'), + 'class' => array('search-global'), + ), + ), + '#weight' => 200, + // Custom CSS, JS or a library can be associated with the toolbar item. + '#attached' => array( + 'css' => array( + drupal_get_path('module', 'search') . '/css/search.base.css', + ), + ), + ); + + // The 'Home' tab is a simple link, which is wrapped in markup associated + // with a visual tab styling. $items['home'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => t('Home'), - 'href' => '', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('Home page'), + '#type' => 'link', + '#title' => t('Home'), + '#href' => '', + '#options' => array( + 'attributes' => array( + 'title' => t('Home page'), + 'class' => array('icon', 'icon-home'), + ), ), ), - 'weight' => -10, + '#weight' => -20, ); - /** - * A tab may be associated with a tray. - * - * The tray should contain a renderable array. An option #heading property - * can be passed. The text is written to a heading tag in the tray as a - * landmark for accessibility. A default heading will be created if one is not - * passed. - */ + // A tray may be associated with a tab. + // + // When the tab is activated, the tray will become visible, either in a + // horizontal or vertical orientation on the screen. + // + // The tray should contain a renderable array. An optional #heading property + // can be passed. This text is written to a heading tag in the tray as a + // landmark for accessibility. $items['commerce'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => t('Shopping cart'), - 'href' => '', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('Shopping cart'), + '#type' => 'link', + '#title' => t('Shopping cart'), + '#href' => '/cart', + '#options' => array( + 'html' => FALSE, + 'attributes' => array( + 'title' => t('Shopping cart'), + ), ), ), 'tray' => array( '#heading' => t('Shopping cart actions'), - 'content' => array( + 'shopping_cart' => array( '#theme' => 'item_list', '#items' => array( /* An item list renderable array */ ), ), ), - 'weight' => 50, + '#weight' => 150, + ); + + // The 'tray' can be used to render arbritrary content. + // + // The 'tray' is rendered outside the administration bar but within the + // containing toolbar element. + // + // If the default behavior and styling of a toolbar tray is not desired, one + // can render content to the toolbar element and apply custom theming and + // behaviors. + $items['user_messages'] = array( + // Include the toolbar_tab_wrapper to style the link like a toolbar tab. + // Exclude the theme wrapper if custom styling is desired. + '#type' => 'toolbar_item', + 'tab' => array( + '#type' => 'link', + '#theme' => 'user_message_toolbar_tab', + '#theme_wrappers' => array(), + '#title' => t('Messages'), + '#href' => '/user/messages', + '#options' => array( + 'attributes' => array( + 'title' => t('Messages'), + ), + ), + ), + 'tray' => array( + '#heading' => t('User messages'), + 'messages' => array(/* renderable content */), + ), + '#weight' => 125, ); return $items; } /** - * Alter the Toolbar menu after hook_toolbar() is invoked. + * Alter the toolbar menu after hook_toolbar() is invoked. * * This hook is invoked by toolbar_view() immediately after hook_toolbar(). The * toolbar definitions are passed in by reference. Each element of the $items @@ -94,11 +156,11 @@ function hook_toolbar() { * may be added, or existing items altered. * * @param $items - * Associative array of Toolbar menu definitions returned from hook_toolbar(). + * Associative array of toolbar menu definitions returned from hook_toolbar(). */ function hook_toolbar_alter(&$items) { // Move the User tab to the right. - $items['commerce']['weight'] = 5; + $items['commerce']['#weight'] = 5; } /** diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index e543326..a777a27 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -19,7 +19,7 @@ function toolbar_help($path, $arg) { $output .= '

' . t('Uses') . '

'; $output .= '
'; $output .= '
' . t('Displaying administrative links') . '
'; - $output .= '
' . t('The Toolbar module displays a bar containing top-level administrative links across the top of the screen. Below that, the Toolbar module has a drawer section where it displays links provided by other modules, such as the core Shortcut module. The drawer can be hidden/shown by clicking on its corresponding tab.', array('@shortcuts-help' => url('admin/help/shortcut'))) . '
'; + $output .= '
' . t('The Toolbar module displays a bar containing top-level administrative components across the top of the screen. Below that, the Toolbar module has a drawer section where it displays links provided by other modules, such as the core Shortcut module. The drawer can be hidden/shown by clicking on its corresponding tab.', array('@shortcuts-help' => url('admin/help/shortcut'))) . '
'; $output .= '
'; return $output; } @@ -41,8 +41,19 @@ function toolbar_permission() { */ function toolbar_theme($existing, $type, $theme, $path) { $items['toolbar'] = array( - 'render element' => 'toolbar', - 'template' => 'toolbar', + 'render element' => 'element', + ); + $items['toolbar_item'] = array( + 'render element' => 'element', + ); + $items['toolbar_tab_wrapper'] = array( + 'render element' => 'element', + ); + $items['toolbar_tray_wrapper'] = array( + 'render element' => 'element', + ); + $items['toolbar_tray_heading_wrapper'] = array( + 'render element' => 'element', ); return $items; } @@ -62,6 +73,54 @@ function toolbar_menu() { } /** + * Implements hook_element_info(). + */ +function toolbar_element_info() { + $elements = array(); + + $elements['toolbar'] = array( + // Do not build the toolbar if the user does not have access. + '#access' => user_access('access toolbar'), + '#pre_render' => array('toolbar_pre_render'), + '#theme' => 'toolbar', + '#attached' => array( + 'library' => array( + array('toolbar', 'toolbar'), + ), + ), + // Metadata for the toolbar wrapping element. + '#attributes' => array( + // The id cannot be simply "toolbar" or it will clash with the simpletest + // tests listing which produces a checkbox with attribute id="toolbar" + 'id' => 'toolbar-administration', + // The 'overlay-displace-top' class pushes the overlay down, so it appears + // below the toolbar. + 'class' => array('toolbar', 'overlay-displace-top'), + 'role' => 'navigation', + ), + // Metadata for the administration bar. + '#bar' => array( + '#heading' => t('Toolbar items'), + '#attributes' => array( + 'id' => 'toolbar-bar', + 'class' => array('bar', 'overlay-displace-top', 'clearfix'), + ), + ), + ); + + // A toolbar item is wrapped in markup for common styling. The 'tray' + // property contains a renderable array. theme_toolbar_tab() is a light + // wrapper around the l() function. The contents of tray are rendered in + // theme_toolbar_tab(). + $elements['toolbar_item'] = array( + '#pre_render' => array('toolbar_pre_render_item'), + '#theme' => 'toolbar_item', + '#theme_wrappers' => array('toolbar_tab_wrapper'), + ); + return $elements; +} + +/** * Access callback: Returns if the user has access to the rendered subtree requested by the hash. * * @see toolbar_menu(). @@ -130,60 +189,228 @@ function _toolbar_initialize_page_cache() { */ function toolbar_page_build(&$page) { $page['page_top']['toolbar'] = array( - '#pre_render' => array('toolbar_pre_render'), - '#access' => user_access('access toolbar'), + '#type' => 'toolbar', ); } /** - * Provides pre_render function for the toolbar. + * Builds the Toolbar as a structured array ready for drupal_render(). * * Since building the toolbar takes some time, it is done just prior to * rendering to ensure that it is built only if it will be displayed. * + * @param array $element + * A renderable array. + * + * @return + * A renderable array. + * * @see toolbar_page_build(). */ -function toolbar_pre_render($toolbar) { - $toolbar = array_merge($toolbar, toolbar_view()); - return $toolbar; +function toolbar_pre_render($element) { + + // Get the configured breakpoints to switch from vertical to horizontal + // toolbar presentation. + $breakpoints = entity_load('breakpoint_group', 'module.toolbar.toolbar'); + if (!empty($breakpoints)) { + $media_queries = array(); + $media_queries['toolbar']['breakpoints'] = array_map( + function ($object) { + return $object->mediaQuery; + }, + $breakpoints->breakpoints + ); + + $element['#attached']['js'][] = array( + 'data' => $media_queries, + 'type' => 'setting', + ); + } + + // Get toolbar items from all modules that implement hook_toolbar(). + $items = module_invoke_all('toolbar'); + // Allow for altering of hook_toolbar(). + drupal_alter('toolbar', $items); + // Sort the children. + uasort($items, 'element_sort'); + + // Merge in the original toolbar values. + $element = array_merge($element, $items); + + // Render the children. + $element['#children'] = drupal_render_children($element); + + return $element; } /** - * Implements template_preprocess_HOOK(). + * Returns HTML that wraps the administration toolbar. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties and children of + * the tray. Properties used: #children, #attributes and #bar. */ -function template_preprocess_toolbar(&$variables) { - // Metadata for the toolbar wrapping element. - $variables['attributes'] = new Attribute(array( - // The id cannot be simply "toolbar" or it will clash with the simpletest - // tests listing which produces a checkbox with attribute id="toolbar" - 'id' => 'toolbar-administration', - // The 'overlay-displace-top' class pushes the overlay down, so it appears - // below the toolbar. - 'class' => array('toolbar', 'overlay-displace-top'), - 'role' => 'navigation' - )); - - // Provide a convenience variable for the themed tabs. - $variables['toolbar']['tabs']['#attributes']['class'][] = 'overlay-displace-top'; - $variables['tabs'] = $variables['toolbar']['tabs']; - - // Place the tabs in a top-level variable so that attribute information - // can be associated with each one. - foreach ($variables['toolbar']['trays'] as $key => $tray) { - // Create tray heading text. Prefer the provided heading text if it exists. - $heading = isset($tray['#heading']) ? $tray['#heading'] : t('@group actions', array('@group' => $key)); - - $variables['trays'][$key] = array( - 'heading' => $heading, - 'content' => $variables['toolbar']['trays'][$key], - 'attributes' => new Attribute(array( - 'id' => 'toolbar-tray-' . $key, - 'class' => array('tray', 'overlay-displace-top', $key), - 'data-toolbar-tray' => $key, - 'aria-owned-by' => 'toolbar-tab-' . $key, - ) - ), +function theme_toolbar(&$variables) { + if (!empty($variables['element']['#children'])) { + $element = $variables['element']; + $trays = ''; + foreach (element_children($element) as $key) { + $trays .= drupal_render($element[$key]['tray']); + } + return '' + . '' + . '

' . $element['#bar']['#heading'] . '

' + . $element['#children'] . '' . $trays . ''; + } +} + +/** + * Provides markup for associating a tray trigger with a tray element. + * + * A tray is a responsive container that wraps renderable content. Trays present + * content well on small and large screens alike. + * + * @param array $element + * A renderable array. + * + * @return + * A renderable array. + */ +function toolbar_pre_render_item($element) { + + // Attributes are nested in the #options property passed to l(). + // To make the return structure of toolbar_pre_render_item() uniform, + // attributes will always be returned in the #attributes property. These + // properties will be included in the #options parameter passed to l() in + // theme_toolbar_tab(). + $element['#attributes'] = $element['tab']['#options']['attributes']; + + // If tray content is present, markup the tray and its associated trigger. + if (!empty($element['tray'])) { + + // Trays are associated with their trigger by a unique ID. + $id = drupal_html_id('toolbar-tray'); + $element['tab']['#tray_id'] = $id; + + // Provide attributes for a tray trigger. + $attributes = array( + 'id' => 'toolbar-tab-' . $id, + 'data-toolbar-tray' => $id, + 'aria-owns' => $id, + 'role' => 'button', + 'aria-pressed' => 'false', + ); + // Merge in module-provided attributes. + $element['tab'] += array('#attributes' => array()); + $element['tab']['#attributes'] += $attributes; + $element['tab']['#attributes']['class'][] = 'trigger'; + + // Provide attributes for the tray theme wrapper. + $attributes = array( + 'id' => $id, + 'data-toolbar-tray' => $id, + 'aria-owned-by' => 'toolbar-tab-' . $id, ); + // Merge in module-provided attributes. + if (!isset($element['tray']['#wrapper_attributes'])) { + $element['tray']['#wrapper_attributes'] = array(); + } + $element['tray']['#wrapper_attributes'] += $attributes; + $element['tray']['#wrapper_attributes']['class'][] = 'tray'; + $element['tray']['#wrapper_attributes']['class'][] = 'overlay-displace-top'; + + if (!isset($element['tray']['#theme_wrappers'])) { + $element['tray']['#theme_wrappers'] = array(); + } + // Add the standard theme_wrapper for trays. + array_unshift($element['tray']['#theme_wrappers'], 'toolbar_tray_wrapper'); + // If a #heading is provided for the tray, provided a #theme_wrapper + // function to append it. + if (!empty($element['tray']['#heading'])) { + array_unshift($element['tray']['#theme_wrappers'], 'toolbar_tray_heading_wrapper'); + } + } + + return $element; +} + +/** + * Implements template_preprocess_HOOK(). + */ +function template_preprocess_toolbar_tab_wrapper(&$variables) { + if (!isset($variables['element']['#wrapper_attributes'])) { + $variables['element']['#wrapper_attributes'] = array(); + } + $variables['element']['#wrapper_attributes']['class'][] = 'tab'; +} + +/** + * Returns HTML for a toolbar item. + * + * This theme function only renders the tab portion of the toolbar item. The + * tray portion will be rendered later. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties and children of + * the tray. + * + * @see toolbar_pre_render_item(). + * @see theme_toolbar(). + */ +function theme_toolbar_item(&$variables) { + return drupal_render($variables['element']['tab']); +} + +/** + * Returns HTML for wrapping a toolbar tab. + * + * Toolbar tabs have a common styling and placement with the toolbar's bar area. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties and children of + * the tray. Properties used: #children and #attributes. + */ +function theme_toolbar_tab_wrapper(&$variables) { + if (!empty($variables['element']['#children'])) { + $element = $variables['element']; + return '' . $element['#children'] . ''; + } +} + +/** + * Returns HTML for wrapping a toolbar tray. + * + * Used in combination with theme_toolbar_tab() to create an + * association between a link tag in the administration bar and a tray. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties and children of + * the tray. Properties used: #children, #toolbar_identifier and + * #attributes. + */ +function theme_toolbar_tray_wrapper(&$variables) { + if (!empty($variables['element']['#children'])) { + $element = $variables['element']; + return '
' . $element['#children'] . '
'; + } +} + +/** + * Returns HTML for prepending a heading to a toolbar tray. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties and children of + * the tray. Properties used: #children and #heading. + */ +function theme_toolbar_tray_heading_wrapper(&$variables) { + if (!empty($variables['element']['#children'])) { + $element = $variables['element']; + return '

' . $element['#heading'] . '

' . $element['#children']; } } @@ -211,16 +438,19 @@ function toolbar_toolbar() { // The 'Home' tab is a simple link, with no corresponding tray. $items['home'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => t('Home'), - 'href' => '', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('Home page'), - 'class' => array('icon', 'icon-home'), + '#type' => 'link', + '#title' => t('Home'), + '#href' => '', + '#options' => array( + 'attributes' => array( + 'title' => t('Home page'), + 'class' => array('icon', 'icon-home'), + ), ), ), - 'weight' => -20, + '#weight' => -20, ); // Retrieve the administration menu from the database. @@ -230,14 +460,14 @@ function toolbar_toolbar() { toolbar_menu_navigation_links($tree); $menu = array( + '#heading' => t('Administration menu'), 'toolbar_administration' => array( '#type' => 'container', '#attributes' => array( - 'class' => array('toolbar-menu',), + 'class' => array('toolbar-menu-administration'), ), 'administration_menu' => menu_tree_output($tree), ), - '#heading' => t('Administration menu'), ); // To conserve bandwidth, we only include the top-level links in the HTML. @@ -247,110 +477,29 @@ function toolbar_toolbar() { // @see toolbar_subtrees_jsonp() $menu['toolbar_administration']['#attached']['js'][url('toolbar/subtrees/' . _toolbar_get_subtree_hash())] = array('type' => 'external'); + // The administration element has a link that is themed to correspond to + // a toolbar tray. The tray contains the full administrative menu of the site. $items['administration'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => t('Menu'), - 'href' => 'admin', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('Admin menu'), - 'class' => array('icon', 'icon-menu'), + '#type' => 'link', + '#title' => t('Menu'), + '#href' => 'admin', + '#options' => array( + 'attributes' => array( + 'title' => t('Admin menu'), + 'class' => array('icon', 'icon-menu'), + ), ), ), 'tray' => $menu, - 'weight' => -15, + '#weight' => -15, ); return $items; } /** - * Builds the Toolbar as a structured array ready for drupal_render(). - * - * @return - * A renderable arrray, with two children: - * - 'tabs': an array of links, rendered by theme('links'). - * - 'trays': an array of render elements displayed when the corresponding tab - * is activated. - */ -function toolbar_view() { - - $build = array('#theme' => 'toolbar'); - $build['#attached']['library'][] = array('toolbar', 'toolbar'); - - // Get the configured breakpoint to switch from vertical to horizontal - // toolbar presentation. - $breakpoints = entity_load('breakpoint_group', 'module.toolbar.toolbar'); - if (!empty($breakpoints)) { - $media_queries = array(); - $media_queries['toolbar']['breakpoints'] = array_map( - function($object) {return $object->mediaQuery;}, - $breakpoints->breakpoints); - - $build['#attached']['js'][] = array( - 'data' => $media_queries, - 'type' => 'setting', - ); - // // Load the breakpoints for toolbar. - foreach ($breakpoints->breakpoints as $key => $breakpoint) { - $build['#attached']['js'][0]['data']['toolbar']['breakpoints'][$key] = $breakpoint->mediaQuery; - } - } - - // Get toolbar items from all modules that implement hook_toolbar() or - // hook_toolbar_alter(). - $toolbar_groups = module_invoke_all('toolbar'); - drupal_alter('toolbar', $toolbar_groups); - uasort($toolbar_groups, 'drupal_sort_weight'); - - // Build the tabs and trays from the toolbar groups. - $build['trays'] = array(); - $build['tabs'] = array( - '#theme' => 'links', - '#links' => array(), - '#attributes' => array( - 'class' => array('bar', 'clearfix'), - ), - '#heading' => array('text' => t('Toolbar'), 'level' => 'h2', 'class' => 'element-invisible'), - ); - $tab_defaults = array( - 'title' => '', - 'href' => '', - 'html' => FALSE, - 'attributes' => new Attribute(), - ); - - foreach ($toolbar_groups as $group => $items) { - if ($tab = $items['tab']) { - // Merge in the defaults. - $tab += $tab_defaults; - } - // Register a tray if one is associated with this tab. - if (!empty($items['tray'])) { - // Provide an id, a data attribute linking each tab to the corresponding - // tray and aria information. - $tab['attributes']['id'] = 'toolbar-tab-' . $group; - $tab['attributes']['data-toolbar-tray'] = $group; - $tab['attributes']['aria-owns'] = 'toolbar-tray-' . $group; - $tab['attributes']['role'] = 'button'; - $tab['attributes']['aria-pressed'] = 'false'; - - if (array_key_exists($group, $build['trays'])) { - array_merge($build['trays'][$group], $items['tray']); - } - else { - // Assign the tray to the build. - $build['trays'][$group] = $items['tray']; - } - } - // Assign the tabs to the build. - $build['tabs']['#links'][$group] = $tab; - } - - return $build; -} - -/** * Gets only the top level items below the 'admin' path. * * @return diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 89bc6c1..1ca66b9 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -2715,29 +2715,30 @@ function user_toolbar() { ); } - $user_tray = array( - '#heading' => t('User account actions'), - 'content' => array( - '#theme' => 'links__toolbar_user', - '#links' => $links, - '#attributes' => array( - 'class' => array('menu',), - ), - ), - ); - $items['user'] = array( + '#type' => 'toolbar_item', 'tab' => array( - 'title' => user_format_name($user), - 'href' => 'user', - 'html' => FALSE, - 'attributes' => array( - 'title' => t('My account'), - 'class' => array('icon', 'icon-user'), + '#type' => 'link', + '#title' => user_format_name($user), + '#href' => 'user', + '#options' => array( + 'attributes' => array( + 'title' => t('My account'), + 'class' => array('icon', 'icon-user'), + ), + ), + ), + 'tray' => array( + '#heading' => t('User account actions'), + 'user_links' => array( + '#theme' => 'links__toolbar_user', + '#links' => $links, + '#attributes' => array( + 'class' => array('menu'), + ), ), ), - 'tray' => $user_tray, - 'weight' => 100, + '#weight' => 100, ); return $items;