Commit b67adf08 authored by lauriii's avatar lauriii

Issue #2542050 by droplet, nod_, dpacassi, Wim Leers, Sam152,...

Issue #2542050 by droplet, nod_, dpacassi, Wim Leers, Sam152, andrewmacpherson, tameeshb, jonathanshaw, lokapujya, Daniel Schaefer, Chi, geerlingguy, tarekdj: Toolbar implementation creates super annoying re-rendering
parent 1239197d
......@@ -13,9 +13,16 @@
position: relative;
width: auto;
}
.toolbar .toolbar-tray-horizontal .toolbar-menu .toolbar-handle,
.toolbar .toolbar-tray-horizontal .toolbar-menu ul,
.toolbar .toolbar-tray-vertical .toolbar-menu ul {
/**
* Hidden horizontal toolbar handle icon.
*/
.toolbar .toolbar-tray-horizontal .toolbar-menu .toolbar-handle {
display: none;
}
/**
* Hidden toolbar sub-menus by default.
*/
.toolbar-tray-open .toolbar-menu .menu-item--expanded ul {
display: none;
}
.toolbar .toolbar-tray-vertical li.open > ul {
......
......@@ -21,7 +21,9 @@
display: none;
}
}
.toolbar-loading #toolbar-administration {
overflow: hidden;
}
/**
* Very specific overrides for Drupal system CSS.
*/
......@@ -56,18 +58,44 @@
position: relative;
z-index: 1250;
}
.toolbar-horizontal .toolbar-tray {
position: fixed;
width: 100%;
left: 0;
}
/* Position the admin toolbar absolutely when the configured standard breakpoint
* is active. The toolbar container, that contains the bar and the trays, is
* position absolutely so that it scrolls with the page. Otherwise, on smaller
* screens, the components of the admin toolbar are positioned statically. */
body.toolbar-fixed .toolbar-oriented,
.toolbar-oriented .toolbar-bar,
.toolbar-oriented .toolbar-tray {
.toolbar-oriented .toolbar-bar {
left: 0;
position: absolute;
right: 0;
top: 0;
}
.toolbar-oriented .toolbar-tray {
left: 0;
position: absolute;
right: 0;
}
/* .toolbar-loading is required by toolbar JavaScript to pre-render markup
* style to avoid extra reflow & flicker. */
@media (min-width: 61em) {
.toolbar-loading.toolbar-horizontal .toolbar .toolbar-bar .toolbar-tab:last-child .toolbar-tray {
position: relative;
display: block;
z-index: -999;
visibility: hidden;
width: 1px;
}
.toolbar-loading.toolbar-horizontal .toolbar .toolbar-bar .toolbar-tab:last-child .toolbar-tray .toolbar-lining {
width: 999em;
}
.toolbar-loading.toolbar-horizontal .toolbar .toolbar-bar .home-toolbar-tab + .toolbar-tab .toolbar-tray {
display: block;
}
}
/* Layer the bar just above the trays and above contextual link triggers. */
.toolbar-oriented .toolbar-bar {
z-index: 502;
......@@ -85,13 +113,16 @@ body.toolbar-tray-open.toolbar-fixed.toolbar-vertical .toolbar-oriented {
width: 240px;
width: 15rem;
}
/* Present the admin toolbar tabs horizontally as a default on user agents that
* do not understand media queries or on user agents where JavaScript is
* disabled. */
.toolbar-loading.toolbar-horizontal .toolbar .toolbar-tray .toolbar-menu > li,
.toolbar .toolbar-bar .toolbar-tab,
.toolbar .toolbar-tray-horizontal li {
float: left; /* LTR */
}
[dir="rtl"] .toolbar-loading.toolbar-horizontal .toolbar .toolbar-tray .toolbar-menu > li,
[dir="rtl"] .toolbar .toolbar-bar .toolbar-tab,
[dir="rtl"] .toolbar .toolbar-tray-horizontal li {
float: right;
......@@ -179,15 +210,10 @@ body.toolbar-tray-open.toolbar-fixed.toolbar-vertical .toolbar-oriented {
.toolbar-oriented .toolbar-tray-horizontal .menu-item ul {
display: none;
}
/* When the configured standard breakpoint is active and the tray is in a
* horizontal position, the tray is fixed to the top of the viewport and does
* not scroll with the page contents. */
body.toolbar-fixed .toolbar .toolbar-tray-horizontal {
position: fixed;
}
/* When the configured standard breakpoint is active and the tray is in a
* vertical position, the tray does not scroll with the page. The contents of
* the tray scroll within the confines of the viewport. */
* the tray scroll within the confines of the viewport.
*/
.toolbar .toolbar-tray-vertical.is-active,
body.toolbar-fixed .toolbar .toolbar-tray-vertical {
height: 100%;
......@@ -258,3 +284,13 @@ body.toolbar-tray-open.toolbar-vertical.toolbar-fixed {
[dir="rtl"] .toolbar-oriented .toolbar-tray-vertical .toolbar-toggle-orientation {
float: left;
}
/**
* Toolbar home button toggle.
*/
.toolbar .toolbar-bar .home-toolbar-tab {
display: none;
}
.path-admin .toolbar-bar .home-toolbar-tab {
display: block;
}
......@@ -58,10 +58,10 @@
.toolbar .toolbar-tray {
background-color: #ffffff;
}
.toolbar .toolbar-tray-horizontal > .toolbar-lining {
.toolbar-horizontal .toolbar-tray > .toolbar-lining {
padding-right: 5em; /* LTR */
}
[dir="rtl"] .toolbar .toolbar-tray-horizontal > .toolbar-lining {
[dir="rtl"] .toolbar-horizontal .toolbar-tray > .toolbar-lining {
padding-right: 0;
padding-left: 5em;
}
......@@ -75,11 +75,11 @@
border-right: 0 none;
box-shadow: 1px 0 5px 2px rgba(0, 0, 0, 0.3333);
}
.toolbar .toolbar-tray-horizontal {
.toolbar-horizontal .toolbar-tray {
border-bottom: 1px solid #aaaaaa;
box-shadow: -2px 1px 3px 1px rgba(0, 0, 0, 0.3333); /* LTR */
}
[dir="rtl"] .toolbar .toolbar-tray-horizontal {
[dir="rtl"] .toolbar-horizontal .toolbar-tray {
box-shadow: 2px 1px 3px 1px rgba(0, 0, 0, 0.3333);
}
.toolbar .toolbar-tray-horizontal .toolbar-tray {
......@@ -101,17 +101,17 @@
.toolbar .toolbar-menu {
background-color: #ffffff;
}
.toolbar .toolbar-tray-horizontal .menu-item + .menu-item {
.toolbar-horizontal .toolbar-tray .menu-item + .menu-item {
border-left: 1px solid #dddddd; /* LTR */
}
[dir="rtl"] .toolbar .toolbar-tray-horizontal .menu-item + .menu-item {
[dir="rtl"] .toolbar-horizontal .toolbar-tray .menu-item + .menu-item {
border-left: 0 none;
border-right: 1px solid #dddddd;
}
.toolbar .toolbar-tray-horizontal .menu-item:last-child {
.toolbar-horizontal .toolbar-tray .menu-item:last-child {
border-right: 1px solid #dddddd; /* LTR */
}
[dir="rtl"] .toolbar .toolbar-tray-horizontal .menu-item:last-child {
[dir="rtl"] .toolbar-horizontal .toolbar-tray .menu-item:last-child {
border-left: 1px solid #dddddd;
}
.toolbar .toolbar-tray-vertical .menu-item + .menu-item {
......@@ -149,10 +149,10 @@
padding: 0;
height: 100%;
}
.toolbar .toolbar-tray-horizontal .toolbar-toggle-orientation {
.toolbar-horizontal .toolbar-tray .toolbar-toggle-orientation {
border-left: 1px solid #c9c9c9; /* LTR */
}
[dir="rtl"] .toolbar .toolbar-tray-horizontal .toolbar-toggle-orientation {
[dir="rtl"] .toolbar-horizontal .toolbar-tray .toolbar-toggle-orientation {
border-left: 0 none;
border-right: 1px solid #c9c9c9;
}
......
......@@ -40,7 +40,6 @@
else {
$toolbarEscape.text(Drupal.t('Home'));
}
$toolbarEscape.closest('.toolbar-tab').removeClass('hidden');
}
}
};
......
......@@ -26,7 +26,6 @@
} else {
$toolbarEscape.text(Drupal.t('Home'));
}
$toolbarEscape.closest('.toolbar-tab').removeClass('hidden');
}
}
};
......
......@@ -88,7 +88,7 @@
*
* @type {string}
*/
orientation: 'vertical',
orientation: 'horizontal',
/**
* A tray is locked if a user toggled it to vertical. Otherwise a tray
......@@ -105,7 +105,7 @@
*
* @type {bool}
*/
isTrayToggleVisible: false,
isTrayToggleVisible: true,
/**
* The height of the toolbar.
......
......@@ -23,11 +23,11 @@
isViewportOverflowConstrained: false,
orientation: 'vertical',
orientation: 'horizontal',
locked: false,
isTrayToggleVisible: false,
isTrayToggleVisible: true,
height: null,
......
......@@ -51,9 +51,26 @@
// Establish the toolbar models and views.
var model = Drupal.toolbar.models.toolbarModel = new Drupal.toolbar.ToolbarModel({
locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')) || false,
activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')))
locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')),
activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID'))),
height: $('#toolbar-administration').outerHeight()
});
// Attach a listener to the configured media query breakpoints.
// Executes it before Drupal.toolbar.views to avoid extra rendering.
for (var label in options.breakpoints) {
if (options.breakpoints.hasOwnProperty(label)) {
var mq = options.breakpoints[label];
var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
// Curry the model and the label of the media query breakpoint to
// the mediaQueryChangeHandler function.
mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
// Fire the mediaQueryChangeHandler for each configured breakpoint
// so that they process once.
Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
}
}
Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView({
el: this,
model: model,
......@@ -69,6 +86,11 @@
model: model
});
// Force layout render to fix mobile view. Only needed on load, not
// for every media query match.
model.trigger('change:isFixed', model, model.get('isFixed'));
model.trigger('change:activeTray', model, model.get('activeTray'));
// Render collapsible menus.
var menuModel = Drupal.toolbar.models.menuModel = new Drupal.toolbar.MenuModel();
Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView({
......@@ -88,20 +110,6 @@
model.set('areSubtreesLoaded', true);
});
// Attach a listener to the configured media query breakpoints.
for (var label in options.breakpoints) {
if (options.breakpoints.hasOwnProperty(label)) {
var mq = options.breakpoints[label];
var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
// Curry the model and the label of the media query breakpoint to
// the mediaQueryChangeHandler function.
mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
// Fire the mediaQueryChangeHandler for each configured breakpoint
// so that they process once.
Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
}
}
// Trigger an initial attempt to load menu subitems. This first attempt
// is made after the media query handlers have had an opportunity to
// process. The toolbar starts in the vertical orientation by default,
......@@ -213,7 +221,7 @@
case 'toolbar.wide':
model.set({
orientation: ((mql.matches) ? 'horizontal' : 'vertical')
orientation: ((mql.matches && !model.get('locked')) ? 'horizontal' : 'vertical')
}, {validate: true});
// The tray orientation toggle visibility does not need to be
// validated.
......
......@@ -30,9 +30,22 @@
$(context).find('#toolbar-administration').once('toolbar').each(function () {
var model = Drupal.toolbar.models.toolbarModel = new Drupal.toolbar.ToolbarModel({
locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')) || false,
activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')))
locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')),
activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID'))),
height: $('#toolbar-administration').outerHeight()
});
for (var label in options.breakpoints) {
if (options.breakpoints.hasOwnProperty(label)) {
var mq = options.breakpoints[label];
var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
}
}
Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView({
el: this,
model: model,
......@@ -48,6 +61,9 @@
model: model
});
model.trigger('change:isFixed', model, model.get('isFixed'));
model.trigger('change:activeTray', model, model.get('activeTray'));
var menuModel = Drupal.toolbar.models.menuModel = new Drupal.toolbar.MenuModel();
Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView({
el: $(this).find('.toolbar-menu-administration').get(0),
......@@ -63,17 +79,6 @@
model.set('areSubtreesLoaded', true);
});
for (var label in options.breakpoints) {
if (options.breakpoints.hasOwnProperty(label)) {
var mq = options.breakpoints[label];
var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
}
}
Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
$(document).on('drupalViewportOffsetChange.toolbar', function (event, offsets) {
......@@ -127,7 +132,7 @@
case 'toolbar.wide':
model.set({
orientation: mql.matches ? 'horizontal' : 'vertical'
orientation: mql.matches && !model.get('locked') ? 'horizontal' : 'vertical'
}, { validate: true });
model.set({
......
......@@ -8,7 +8,6 @@
'use strict';
Drupal.toolbar.BodyVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.BodyVisualView# */{
/**
* Adjusts the body element with the toolbar position and dimension changes.
*
......@@ -17,36 +16,25 @@
* @augments Backbone.View
*/
initialize: function () {
this.listenTo(this.model, 'change:orientation change:offsets change:activeTray change:isOriented change:isFixed change:isViewportOverflowConstrained', this.render);
this.listenTo(this.model, 'change:activeTray ', this.render);
this.listenTo(this.model, 'change:isFixed change:isViewportOverflowConstrained', this.isToolbarFixed);
},
isToolbarFixed: function () {
// When the toolbar is fixed, it will not scroll with page scrolling.
var isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
$('body').toggleClass('toolbar-fixed', (isViewportOverflowConstrained || this.model.get('isFixed')));
},
/**
* @inheritdoc
*/
render: function () {
var $body = $('body');
var orientation = this.model.get('orientation');
var isOriented = this.model.get('isOriented');
var isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
$body
// We are using JavaScript to control media-query handling for two
// reasons: (1) Using JavaScript let's us leverage the breakpoint
// configurations and (2) the CSS is really complex if we try to hide
// some styling from browsers that don't understand CSS media queries.
// If we drive the CSS from classes added through JavaScript,
// then the CSS becomes simpler and more robust.
.toggleClass('toolbar-vertical', (orientation === 'vertical'))
.toggleClass('toolbar-horizontal', (isOriented && orientation === 'horizontal'))
// When the toolbar is fixed, it will not scroll with page scrolling.
.toggleClass('toolbar-fixed', (isViewportOverflowConstrained || this.model.get('isFixed')))
$('body')
// Toggle the toolbar-tray-open class on the body element. The class is
// applied when a toolbar tray is active. Padding might be applied to
// the body element to prevent the tray from overlapping content.
.toggleClass('toolbar-tray-open', !!this.model.get('activeTray'))
// Apply padding to the top of the body to offset the placement of the
// toolbar bar element.
.css('padding-top', this.model.get('offsets').top);
.toggleClass('toolbar-tray-open', !!this.model.get('activeTray'));
}
});
......
......@@ -11,16 +11,17 @@
Drupal.toolbar.BodyVisualView = Backbone.View.extend({
initialize: function initialize() {
this.listenTo(this.model, 'change:orientation change:offsets change:activeTray change:isOriented change:isFixed change:isViewportOverflowConstrained', this.render);
this.listenTo(this.model, 'change:activeTray ', this.render);
this.listenTo(this.model, 'change:isFixed change:isViewportOverflowConstrained', this.isToolbarFixed);
},
render: function render() {
var $body = $('body');
var orientation = this.model.get('orientation');
var isOriented = this.model.get('isOriented');
isToolbarFixed: function isToolbarFixed() {
var isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
$('body').toggleClass('toolbar-fixed', isViewportOverflowConstrained || this.model.get('isFixed'));
},
$body.toggleClass('toolbar-vertical', orientation === 'vertical').toggleClass('toolbar-horizontal', isOriented && orientation === 'horizontal').toggleClass('toolbar-fixed', isViewportOverflowConstrained || this.model.get('isFixed')).toggleClass('toolbar-tray-open', !!this.model.get('activeTray')).css('padding-top', this.model.get('offsets').top);
render: function render() {
$('body').toggleClass('toolbar-tray-open', !!this.model.get('activeTray'));
}
});
})(jQuery, Drupal, Backbone);
\ No newline at end of file
......@@ -52,6 +52,11 @@
*/
onActiveTrayChange: function (model, tray) {
var relevantTray = (tray === null) ? model.previous('activeTray') : tray;
// Current activeTray and previous activeTray are empty, no state change
// to announce.
if (!relevantTray) {
return;
}
var action = (tray === null) ? Drupal.t('closed') : Drupal.t('opened');
var trayNameElement = relevantTray.querySelector('.toolbar-tray-name');
var text;
......
......@@ -25,6 +25,10 @@
onActiveTrayChange: function onActiveTrayChange(model, tray) {
var relevantTray = tray === null ? model.previous('activeTray') : tray;
if (!relevantTray) {
return;
}
var action = tray === null ? Drupal.t('closed') : Drupal.t('opened');
var trayNameElement = relevantTray.querySelector('.toolbar-tray-name');
var text;
......
......@@ -48,6 +48,7 @@
this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render);
this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented', this.updateToolbarHeight);
// Add the tray orientation toggles.
this.$el
......@@ -59,6 +60,32 @@
this.model.trigger('change:activeTab');
},
/**
* Update the toolbar element height.
*
* @constructs
*
* @augments Backbone.View
*/
updateToolbarHeight: function () {
this.model.set('height', $('#toolbar-bar').find('.toolbar-tab').outerHeight() + $('.is-active.toolbar-tray-horizontal').outerHeight());
$('body').css({
'padding-top': this.model.get('height')
});
this.triggerDisplace();
},
// Trigger a recalculation of viewport displacing elements. Use setTimeout
// to ensure this recalculation happens after changes to visual elements
// have processed.
triggerDisplace: function () {
_.defer(function () {
Drupal.displace(true);
});
},
/**
* @inheritdoc
*
......@@ -69,6 +96,9 @@
this.updateTabs();
this.updateTrayOrientation();
this.updateBarAttributes();
$('body').removeClass('toolbar-loading');
// Load the subtrees if the orientation of the toolbar is changed to
// vertical. This condition responds to the case that the toolbar switches
// from horizontal to vertical orientation. The toolbar starts in a
......@@ -84,12 +114,7 @@
if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) {
this.loadSubtrees();
}
// Trigger a recalculation of viewport displacing elements. Use setTimeout
// to ensure this recalculation happens after changes to visual elements
// have processed.
window.setTimeout(function () {
Drupal.displace(true);
}, 0);
return this;
},
......@@ -209,12 +234,20 @@
*/
updateTrayOrientation: function () {
var orientation = this.model.get('orientation');
// The antiOrientation is used to render the view of action buttons like
// the tray orientation toggle.
var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
// Update the orientation of the trays.
// Toggle toolbar's parent classes before other toolbar classes to avoid
// potential flicker and re-rendering.
$('body')
.toggleClass('toolbar-vertical', (orientation === 'vertical'))
.toggleClass('toolbar-horizontal', (orientation === 'horizontal'));
var removeClass = (antiOrientation === 'horizontal') ? 'toolbar-tray-horizontal' : 'toolbar-tray-vertical';
var $trays = this.$el.find('.toolbar-tray')
.removeClass('toolbar-tray-horizontal toolbar-tray-vertical')
.removeClass(removeClass)
.addClass('toolbar-tray-' + orientation);
// Update the tray orientation toggle button.
......@@ -246,14 +279,8 @@
adjustPlacement: function () {
var $trays = this.$el.find('.toolbar-tray');
if (!this.model.get('isOriented')) {
$trays.css('margin-top', 0);
$trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical');
}
else {
// The toolbar container is invisible. Its placement is used to
// determine the container for the trays.
$trays.css('margin-top', this.$el.find('.toolbar-bar').outerHeight());
}
},
/**
......
......@@ -30,24 +30,40 @@
this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render);
this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented', this.updateToolbarHeight);
this.$el.find('.toolbar-tray .toolbar-lining').append(Drupal.theme('toolbarOrientationToggle'));
this.model.trigger('change:activeTab');
},
updateToolbarHeight: function updateToolbarHeight() {
this.model.set('height', $('#toolbar-bar').find('.toolbar-tab').outerHeight() + $('.is-active.toolbar-tray-horizontal').outerHeight());
$('body').css({
'padding-top': this.model.get('height')
});
this.triggerDisplace();
},
triggerDisplace: function triggerDisplace() {
_.defer(function () {
Drupal.displace(true);
});
},
render: function render() {
this.updateTabs();
this.updateTrayOrientation();
this.updateBarAttributes();
$('body').removeClass('toolbar-loading');
if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) {
this.loadSubtrees();
}
window.setTimeout(function () {
Drupal.displace(true);
}, 0);
return this;
},
......@@ -132,7 +148,10 @@
var antiOrientation = orientation === 'vertical' ? 'horizontal' : 'vertical';
var $trays = this.$el.find('.toolbar-tray').removeClass('toolbar-tray-horizontal toolbar-tray-vertical').addClass('toolbar-tray-' + orientation);
$('body').toggleClass('toolbar-vertical', orientation === 'vertical').toggleClass('toolbar-horizontal', orientation === 'horizontal');
var removeClass = antiOrientation === 'horizontal' ? 'toolbar-tray-horizontal' : 'toolbar-tray-vertical';
var $trays = this.$el.find('.toolbar-tray').removeClass(removeClass).addClass('toolbar-tray-' + orientation);
var iconClass = 'toolbar-icon-toggle-' + orientation;
var iconAntiClass = 'toolbar-icon-toggle-' + antiOrientation;
......@@ -152,10 +171,7 @@
adjustPlacement: function adjustPlacement() {
var $trays = this.$el.find('.toolbar-tray');
if (!this.model.get('isOriented')) {
$trays.css('margin-top', 0);
$trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical');
} else {
$trays.css('margin-top', this.$el.find('.toolbar-bar').outerHeight());
}
},
......
......@@ -149,7 +149,7 @@ function toolbar_toolbar() {
],
],
'#wrapper_attributes' => [
'class' => ['hidden', 'home-toolbar-tab'],
'class' => ['home-toolbar-tab'],
],
'#attached' => [
'library' => [
......@@ -276,6 +276,18 @@ function toolbar_menu_navigation_links(array $tree) {
return $tree;
}
/**
* Implements hook_preprocess_HOOK() for HTML document templates.
*/
function toolbar_preprocess_html(&$variables) {
if (!\Drupal::currentUser()->hasPermission('access toolbar')) {
return;
}
$variables['attributes'] = new Attribute($variables['attributes']);
$variables['attributes']->addClass(['toolbar-tray-open', 'toolbar-horizontal', 'toolbar-fixed', 'toolbar-loading']);
}
/**
* Returns the rendered subtree of each top-level toolbar link.
*
......
......@@ -13,9 +13,16 @@
position: relative;
width: auto;
}
.toolbar .toolbar-tray-horizontal .toolbar-menu .toolbar-handle,
.toolbar .toolbar-tray-horizontal .toolbar-menu ul,
.toolbar .toolbar-tray-vertical .toolbar-menu ul {
/**
* Hidden horizontal toolbar handle icon.
*/
.toolbar .toolbar-tray-horizontal .toolbar-menu .toolbar-handle {
display: none;
}
/**
* Hidden toolbar sub-menus by default.
*/
.toolbar-tray-open .toolbar-menu .menu-item--expanded ul {
display: none;
}
.toolbar .toolbar-tray-vertical li.open > ul {
......
......@@ -21,7 +21,9 @@
display: none;
}
}
.toolbar-loading #toolbar-administration {
overflow: hidden;
}
/**