Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions workbench_tabs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/vendor/
9 changes: 9 additions & 0 deletions workbench_tabs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Workbench Tabs

A module to provide local task tabs and messages as part of the core Toolbar, so that custom themes don't need to provide these administrative elements.

## Configuration

* Enable the module
* Grant the "Use Workbench Tabs" permission to one or more roles
* Optionally disable the primary and secondary tabs blocks for the admin theme, so that there are not duplicate task tabs on admin pages
143 changes: 143 additions & 0 deletions workbench_tabs/css/workbench-tabs.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* @file workbench-tabs.css
* CSS for displaying local task tabs below the core toolbar.
*/

/* Container */
.workbench-tabs {
background-color: #FFFFFF;
}

/* Tabs Container */
.workbench-tabs__tabs {
position: relative;
min-height: 33px;
background-color: #F5F5F5;
box-shadow: 0px 2px 10px #888888 inset;
}

/* Tabs lists */
.workbench-tabs__tabs > ul {
list-style: none;
margin: 0;
padding: 0;
text-align: right;
}

/* Tabs Lists Items */
.workbench-tabs__tabs > ul > li {
display: inline-block;
}

/* Primary Tabs default */
.primary-tabs > li > a {
display: inline-block;
padding: 10px 25px;
}

/* Primary Tabs hover and focus */
.primary-tabs > li > a:active,
.primary-tabs > li > a:hover,
.primary-tabs > li > a:focus {
background-color: #FFFFFF;
text-decoration: none;
color: #0032a0; /*Drupal Link color*/
transition: all 0.15s ease-out;
}

/* Primary Tabs Active */
.primary-tabs > li > a.is-active {
background-color: #FFFFFF;
color: #0C2240; /*Drupal hover color*/
box-shadow: 2px 2px 2px #888888;
}

/* Secondary Tabs List (ul) */
.secondary-tabs {
display: block;
border-top: 1px solid #b5b5b5;
background-color: #EBEBEB;
box-shadow: 0px 2px 10px rgba(181, 181, 181, 0.5) inset;
}

/* Secondary Tabs default link */
.secondary-tabs > li > a {
display: inline-block;
padding: 5px 15px;
}

/* Secondary Tabs hover */
.secondary-tabs > li > a:active,
.secondary-tabs > li > a:hover,
.secondary-tabs > li > a:focus {
background-color: rgba(181, 181, 181, 0.5);
color: #0032a0; /*Drupal Link color*/
}

/* Secondary Tabs Active */
.secondary-tabs > li > a.is-active {
background-color: rgba(181, 181, 181, 0.5);
color: #0C2240; /*Drupal hover color*/
}

/* Messages Container */
.workbench-tabs__message {
padding: 10px 20px;
}

/* Messages Container, closed */
.workbench-tabs__message.is-closed {
display: none;
padding: 10px 20px;
}

/* Zap toolbar link styling within the message area. */
.workbench-tabs.toolbar .workbench-tabs__message a {
display: inline;
line-height: inherit;
}

/* Open/Close Messages Trigger */
.workbench-tabs__trigger {
position: absolute;
top: 0;
left: 20px;
display: inline-block;
border-left: 1px solid #b5b5b5;
border-right: 1px solid #b5b5b5;
padding: 8px 25px;
color: #0032a0;
}

/* Place "Hide" in for messaging when the drawer is open */
.workbench-tabs__trigger > .show {
display: none;
}
.workbench-tabs__trigger > .hide {
display: inline;
}

/* Place "Show" in for messaging when the drawer is closed */
.workbench-tabs__trigger.is-closed > .show {
display: inline;
}
.workbench-tabs__trigger.is-closed > .hide {
display: none;
}

/* add in an arrow indicator to the message */
.workbench-tabs__trigger:after {
content: url('../images/icon-arrow.png');
position: relative;
top: 2px;
display: inline-block;
margin-left: 10px;
}

/* Rotate the arrow indicator when the drawer is closed */
.workbench-tabs__trigger.is-closed:after {
-ms-transform: rotate(180deg); /* IE 9 */
-webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */
transform: rotate(180deg);
top: -.5px;
}
Binary file added workbench_tabs/images/icon-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions workbench_tabs/js/workbench_tabs_trigger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @file
* Collapse and expand the workbench_tabs messages.
*/

(function ($) {

'use strict';

var $messageTrigger = $('.workbench-tabs__trigger');
var $messageContents = $('.workbench-tabs__message');

var messageHeight = $messageContents.outerHeight(true);
var messagesOpen = messageHeight > 0;

Drupal.behaviors.workbenchTabs = {};

Drupal.behaviors.workbenchTabs.attach = function() {
// Open/Close functionality for rail navigation.
$messageTrigger.once('workbenchTabsMessagesButtonClick').click(function(e) {
e.preventDefault();
Drupal.behaviors.workbenchTabs.toggleMessagesVisual();
});

// Lose focus on the trigger when the mouse leaves. Using .blur() in the
// click handler breaks menus for users who are tabbing through.
$messageTrigger.once('workbenchTabsMessagesButtonMouseout').mouseout(function() {
$(this).blur();
});

// Close the drawer when we scroll past it.
$(window).once('workbenchTabsMessagesScroll').on('scroll', function() {
if (messagesOpen && $(window).scrollTop() > messageHeight) {

// Reevaluate message height because user interactions can change it,
// but we don't want to calculate this on every scroll event.
messageHeight = $messageContents.outerHeight(true);
if ($(window).scrollTop() > messageHeight) {
Drupal.behaviors.workbenchTabs.closeMessages();

// Scroll to the top of the page to prevent a jump.
$(window).scrollTop(0);
}
}
});
};

Drupal.behaviors.workbenchTabs.toggleMessagesVisual = function() {
$messageContents.slideToggle('slow', function() {
messagesOpen = $messageContents.is(':visible');
Drupal.behaviors.workbenchTabs.toggleMessages(messagesOpen);
});
}

/**
* @param bool state
* Force opening or closing the messages.
* - true: open messages
* - false: close messages
*/
Drupal.behaviors.workbenchTabs.toggleMessages = function(state) {
if (state === true || state === false) {
messagesOpen = !state;
}
else {
messagesOpen = $messageContents.is(':visible');
}

if (messagesOpen) {
Drupal.behaviors.workbenchTabs.closeMessages();
}
else {
Drupal.behaviors.workbenchTabs.openMessages();
}
}

Drupal.behaviors.workbenchTabs.closeMessages = function() {
$messageTrigger.addClass('is-closed');
$messageContents.addClass('is-closed');
messagesOpen = false;
}

Drupal.behaviors.workbenchTabs.openMessages = function() {
$messageTrigger.removeClass('is-closed');
$messageContents
.removeClass('is-closed')
.attr('style', '');
messageHeight = $messageContents.outerHeight(true);
messagesOpen = true;
}

})(jQuery);
59 changes: 59 additions & 0 deletions workbench_tabs/src/Element/LocalTasks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Drupal\workbench_tabs\Element;

use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\RenderElement;

/**
* Provides a render element for the local task tabs.
*
* @RenderElement("workbench_tabs_local_tasks")
*/
class LocalTasks extends RenderElement {

/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);

return [
'#pre_render' => [[$class, 'preRenderLocalTasks']],
'#theme' => 'workbench_tabs_menu_local_tasks',
'#primary' => '',
'#secondary' => '',
];
}

/**
* @param array $element
* A renderable array.
*
* @return array
* A renderable array.
*/
public static function preRenderLocalTasks($element) {
// This is a workaround for entity pages whose routes have been taken over by
// Page Manager, which breaks local tasks by messing with route names.
$route_name = \Drupal::routeMatch()->getRouteName();
if (preg_match('/^entity\.[^\.]+\.canonical/', $route_name, $matches)) {
$route_name = $matches[0];
}

/** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
$manager = \Drupal::service('plugin.manager.menu.local_task');

foreach (['#primary', '#secondary'] as $i => $key) {
$tabs = $manager->getLocalTasks($route_name, $i);

foreach (Element::getVisibleChildren($tabs['tabs']) as $tab_key) {
$element[$key][$tab_key] = $tabs['tabs'][$tab_key];
$element[$key][$tab_key]['#theme'] = 'workbench_tabs_menu_local_task';
}
}

return $element;
}

}
53 changes: 53 additions & 0 deletions workbench_tabs/src/Element/StatusMessages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Drupal\workbench_tabs\Element;

use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\RenderElement;

/**
* Provides a render element for the status messages.
*
* Unlike Drupal\Core\Render\Element\StatusMessages, this element clears the
* messages when it renders them, so that they are only displayed once on the
* page.
*
* @RenderElement("workbench_tabs_status_messages")
*/
class StatusMessages extends RenderElement {

/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);

return [
'#pre_render' => [[$class, 'renderMessages']],
'#message_type' => NULL,
'#clear_queue' => TRUE,
];
}

/**
* Clear the message queue when rendering the messages.
*/
public static function renderMessages($element) {
$messages = drupal_get_messages($element['#message_type'], $element['#clear_queue']);

if (!empty($messages)) {
$element = [
'#theme' => 'workbench_tabs_status_messages',
'#message_list' => $messages,
'#status_headings' => [
'status' => t('Status message'),
'error' => t('Error message'),
'warning' => t('Warning message'),
],
] + $element;
}

return $element;
}

}
19 changes: 19 additions & 0 deletions workbench_tabs/templates/workbench-tabs-menu-local-task.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{#
/**
* @file
* Theme override for a local task link.
*
* This is a copy of stable/templates/navigation/menu-local-task.html.twig
*
* Available variables:
* - attributes: HTML attributes for the wrapper element.
* - is_active: Whether the task item is an active tab.
* - link: A rendered link element.
*
* Note: This template renders the content for each task item in
* menu-local-tasks.html.twig.
*
* @see template_preprocess_menu_local_task()
*/
#}
<li{{ attributes }}>{{ link }}</li>
Loading