Commit c97d0958 authored by webchick's avatar webchick

Issue #2786459 by tedbow, nod_, martin107, tkoleary, droplet, drpal:...

Issue #2786459 by tedbow, nod_, martin107, tkoleary, droplet, drpal: "Offcanvas" tray should be using the existing dialog system
parent 0d74926e
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
*/ */
/* Position the offcanvas tray container outside the right of the viewport. */ /* Position the offcanvas tray container outside the right of the viewport. */
#offcanvas { .ui-dialog.ui-dialog-offcanvas {
box-sizing: border-box; box-sizing: border-box;
height: 100%; height: 100%;
overflow-y: auto; overflow: hidden;
z-index: 501; z-index: 501;
} }
...@@ -16,49 +16,28 @@ ...@@ -16,49 +16,28 @@
right: 0; right: 0;
} }
/* Position the button that closes the offcanvas tray. */
#offcanvas > button.offcanvasClose {
position: static;
float: right; /* LTR */
height: 52px;
width: 40px;
border: 0;
border-radius: 0;
background: url(../../../misc/icons/bebebe/ex.svg) center center no-repeat;
color: transparent;
cursor: pointer;
z-index: 501;
}
#offcanvas > button.offcanvasClose:focus {
outline: none;
}
[dir="rtl"] #offcanvas > button.offcanvasClose {
float: left;
}
/* Create a place to name the tray. */ /* Create a place to name the tray. */
#offcanvas h1 { .ui-dialog.ui-dialog-offcanvas h1 {
padding: 15px 25% 15px 15px; /* LTR */ padding: 15px 25% 15px 15px; /* LTR */
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
font-size: 120%; font-size: 120%;
} }
[dir="rtl"] #offcanvas h1 { [dir="rtl"] .ui-dialog.ui-dialog-offcanvas h1 {
text-align: right; text-align: right;
padding-right: 0; padding-right: 0;
padding-left: 25%; padding-left: 25%;
} }
/* Wrap the form that's inside the offcanvas tray. */ /* Wrap the form that's inside the offcanvas tray. */
#offcanvas > .offcanvas-content { .ui-dialog.ui-dialog-offcanvas > .ui-dialog-content {
height: 10000px;
padding: 0 15px; padding: 0 15px;
} }
[dir="rtl"] #offcanvas .offcanvas-content { [dir="rtl"] .ui-dialog.ui-dialog-offcanvas .ui-dialog-content {
text-align: right; text-align: right;
} }
#offcanvas > .form-item, .ui-dialog.ui-dialog-offcanvas > .form-item,
#offcanvas > .form-item .form-item { .ui-dialog.ui-dialog-offcanvas > .form-item .form-item {
width: 100%; width: 100%;
} }
...@@ -71,132 +50,32 @@ ...@@ -71,132 +50,32 @@
float: left; float: left;
} }
/* Media queries. */
/* @todo Rework breakpoints: https://www.drupal.org/node/2784599. */
@media (max-width: 700px) {
#offcanvas {
position: absolute;
display: block;
right: 0;
top: 0;
width: 300px;
margin-right: -300px;
padding-top: 39px;
}
/* Wrap the rest of the site so we can control its width. */
#main-canvas-wrapper #main-canvas {
display: inline-block;
width: 100%;
}
#main-canvas-wrapper.js-tray-open #offcanvas {
margin-right: 0;
right: 0;
top: 0;
}
#main-canvas-wrapper.js-tray-open #main-canvas {
position: static;
width: 100%;
}
}
@media (min-width: 700px) {
/* Position the offcanvas tray container outside the right of the viewport. */
#offcanvas {
position: fixed;
display: inline-block;
width: 35%;
-webkit-transform: translateX(100%);
-moz-transform: translateX(100%);
-o-transform: translateX(100%);
-ms-transform: translateX(100%);
transform: translateX(100%);
}
[dir="rtl"] #offcanvas {
text-align: right;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-o-transform: translateX(-100%);
-ms-transform: translateX(-100%);
transform: translateX(-100%);
}
/* Wrap the rest of the site so we can control its width. */
#main-canvas-wrapper #main-canvas {
display: inline-block;
width: 100%;
}
/* Move the offcanvas tray on canvas. */
#main-canvas-wrapper.js-tray-open #offcanvas {
-webkit-transform: translateX(0);
-moz-transform: translateX(0);
-o-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
}
/* Reduce the width of the main canvas to provide space for the offcanvas tray. */
#main-canvas-wrapper.js-tray-open #main-canvas {
width: 65%;
}
}
@media (min-width: 900px) {
/* Position the offcanvas tray container outside the right of the viewport. */
#offcanvas {
position: fixed;
display: inline-block;
width: 30%;
}
/* Wrap the rest of the site so we can control its width. */
#main-canvas-wrapper #main-canvas {
display: inline-block;
width: 100%;
}
/* Reduce the width of the main canvas to provide space for the offcanvas tray. */
#main-canvas-wrapper.js-tray-open #main-canvas {
width: 70%;
}
}
@media (min-width: 1000px) {
/* Position the offcanvas tray container outside the right of the viewport. */
#offcanvas {
position: fixed;
display: inline-block;
width: 25%;
}
/* Wrap the rest of the site so we can control its width. */
#main-canvas-wrapper #main-canvas {
display: inline-block;
width: 100%;
}
/* Reduce the width of the main canvas to provide space for the offcanvas tray. */
#main-canvas-wrapper.js-tray-open #main-canvas {
width: 75%;
}
}
/* /*
* Form layout changes, mostly specific to Bartik theme and menu. * Form layout changes, mostly specific to Bartik theme and menu.
* @todo Remove when more general form styling is done: * @todo Remove when more general form styling is done:
* https://www.drupal.org/node/2784437. * https://www.drupal.org/node/2784437.
*/ */
#offcanvas td { .ui-dialog.ui-dialog-offcanvas td {
width: auto; width: auto;
} }
#offcanvas .menu-enabled { .ui-dialog.ui-dialog-offcanvas .menu-enabled {
width: auto; width: auto;
} }
#offcanvas table#menu-overview th { .ui-dialog.ui-dialog-offcanvas table#menu-overview th {
display: none; display: none;
} }
#offcanvas table#menu-overview tr td:first-child { .ui-dialog.ui-dialog-offcanvas table#menu-overview tr td:first-child {
min-width: 110px; min-width: 110px;
} }
#offcanvas details > .details-wrapper { .ui-dialog.ui-dialog-offcanvas details > .details-wrapper {
padding: 5px; padding: 5px;
overflow: scroll; overflow: scroll;
} }
#offcanvas .tabledrag-toggle-weight { .ui-dialog.ui-dialog-offcanvas .tabledrag-toggle-weight {
font-size: 80%; font-size: 80%;
} }
#offcanvas input:focus, .ui-dialog.ui-dialog-offcanvas input:focus,
#offcanvas summary:focus { .ui-dialog.ui-dialog-offcanvas summary:focus {
outline: none; outline: none;
box-shadow: 2px 2px #ddd; box-shadow: 2px 2px #ddd;
} }
......
...@@ -92,18 +92,18 @@ button.toolbar-icon.toolbar-icon-edit.toolbar-item:hover > .toolbar-icon-edit:be ...@@ -92,18 +92,18 @@ button.toolbar-icon.toolbar-icon-edit.toolbar-item:hover > .toolbar-icon-edit:be
* @todo Move Off-canvas css into core Off-canvas library: * @todo Move Off-canvas css into core Off-canvas library:
* https://www.drupal.org/node/2784443. * https://www.drupal.org/node/2784443.
*/ */
#offcanvas { .ui-dialog.ui-dialog-offcanvas {
background: #fff; background: #fff;
border-left: 1px solid #ddd; /* LTR */ border-left: 1px solid #ddd; /* LTR */
box-shadow: -2px 2px 1px 1px rgba(0, 0, 0, 0.1); /* LTR */ box-shadow: -2px 2px 1px 1px rgba(0, 0, 0, 0.1); /* LTR */
} }
[dir="rtl"] #offcanvas { [dir="rtl"] .ui-dialog.ui-dialog-offcanvas {
border-right: 1px solid #ddd; border-right: 1px solid #ddd;
box-shadow: 2px 2px 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 2px 2px 1px 1px rgba(0, 0, 0, 0.1);
} }
/* Style the tray header. */ /* Style the tray header. */
#offcanvas h1 { .ui-dialog.ui-dialog-offcanvas h1 {
font-size: 120%; font-size: 120%;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
} }
/** /**
* @file * @file
* Drupal's off-canvas library. * Drupal's off-canvas library.
*
* @todo This functionality should extracted into a new core library or a part
* of the current drupal.dialog.ajax library.
* https://www.drupal.org/node/2784443
*/ */
(function ($, Drupal) { (function ($, Drupal, debounce, displace) {
'use strict'; 'use strict';
// Set the initial state of the off-canvas element.
// If the state has been set previously, use it.
Drupal.offCanvas = {
visible: (Drupal.offCanvas ? Drupal.offCanvas.visible : false)
};
/** /**
* Create a wrapper container for the off-canvas element. * The edge of the screen that the dialog should appear on.
* *
* @return {jQuery} * @type {string}
* jQuery object that is the off-canvas wrapper element.
*/ */
Drupal.theme.createOffCanvasWrapper = function createOffCanvasWrapper() { var edge = document.documentElement.dir === 'rtl' ? 'left' : 'right';
return $('<div id="offcanvas" ' + (document.dir === 'ltr' ? 'data-offset-right' : 'data-offset-left') + ' role="region" aria-labelledby="offcanvas-header"></div>');
};
/** /**
* Create the title element for the off-canvas element. * Resets the size of the dialog.
*
* @param {string} title
* The title string.
* *
* @return {object} * @param {jQuery.Event} event
* jQuery object that is the off-canvas title element. * The event triggered.
*/ */
Drupal.theme.createTitle = function createTitle(title) { function resetSize(event) {
return $('<h1 id="offcanvas-header">' + title + '</h1>'); var offsets = displace.offsets;
}; var $element = event.data.$element;
var $widget = $element.dialog('widget');
/** var adjustedOptions = {
* Create the actual off-canvas content. // @see http://api.jqueryui.com/position/
* position: {
* @param {string} data my: edge + ' top',
* This is fully rendered HTML from Drupal. at: edge + ' top' + (offsets.top !== 0 ? '+' + offsets.top : ''),
* of: window
* @return {object} }
* jQuery object that is the off-canvas content element. };
*/
Drupal.theme.createOffCanvasContent = function createOffCanvasContent(data) { $widget.css({
return $('<div class="offcanvas-content">' + data + '</div>'); position: 'fixed',
}; height: ($(window).height() - (offsets.top + offsets.bottom)) + 'px'
});
$element
.dialog('option', adjustedOptions)
.trigger('dialogContentResize.outsidein');
}
/** /**
* Create the off-canvas close element. * Adjusts the dialog on resize.
*
* @param {object} offCanvasWrapper
* The jQuery off-canvas wrapper element
* @param {object} pageWrapper
* The jQuery off page wrapper element
* *
* @return {jQuery} * @param {jQuery.Event} event
* jQuery object that is the off-canvas close element. * The event triggered.
*/ */
Drupal.theme.createOffCanvasClose = function createOffCanvasClose(offCanvasWrapper, pageWrapper) { function handleDialogResize(event) {
return $([ var $element = event.data.$element;
'<button class="offcanvasClose" aria-label="', var $widget = $element.dialog('widget');
Drupal.t('Close configuration tray.'),
'"><span class="visually-hidden">', var $offsets = $widget.find('> :not(#drupal-offcanvas, .ui-resizable-handle)');
Drupal.t('Close'), var offset = 0;
'</span></button>' var modalHeight;
].join(''))
.on('click', function () { // Let scroll element take all the height available.
pageWrapper $element.css({height: 'auto'});
.removeClass('js-tray-open') modalHeight = $widget.height();
.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function () { $offsets.each(function () { offset += $(this).outerHeight(); });
Drupal.offCanvas.visible = false;
offCanvasWrapper.remove();
Drupal.announce(Drupal.t('Configuration tray closed.'));
}
);
});
};
// Take internal padding into account.
var scrollOffset = $element.outerHeight() - $element.height();
$element.height(modalHeight - offset - scrollOffset);
}
/** /**
* Command to open an off-canvas element. * Adjusts the body padding when the dialog is resized.
* *
* @param {Drupal.Ajax} ajax * @param {jQuery.Event} event
* The Drupal Ajax object. * The event triggered.
* @param {object} response
* Object holding the server response.
* @param {number} [status]
* The HTTP status code.
*/ */
Drupal.AjaxCommands.prototype.openOffCanvas = function (ajax, response, status) { function bodyPadding(event) {
// Discover display/viewport size. var $element = event.data.$element;
// @todo Work on breakpoints for tray size: var $widget = $element.dialog('widget');
// https://www.drupal.org/node/2784599. var $body = $('body');
var $pageWrapper = $('#main-canvas-wrapper');
// var pageWidth = $pageWrapper.width(); var width = $widget.outerWidth();
var bodyPadding = $body.css('padding-' + edge);
// Construct off-canvas wrapper if (width !== bodyPadding) {
var $offcanvasWrapper = Drupal.theme('createOffCanvasWrapper'); $body.css('padding-' + edge, width + 'px');
$widget.attr('data-offset-' + edge, width);
// Construct off-canvas internal elements. displace();
var $offcanvasClose = Drupal.theme('createOffCanvasClose', $offcanvasWrapper, $pageWrapper);
var $title = Drupal.theme('createTitle', response.dialogOptions.title);
var $offcanvasContent = Drupal.theme('createOffCanvasContent', response.data);
// Put everything together.
$offcanvasWrapper.append([$offcanvasClose, $title, $offcanvasContent]);
// Handle opening or updating tray with content.
var existingTray = false;
if (Drupal.offCanvas.visible) {
// Remove previous content then append new content.
$pageWrapper.find('#offcanvas').remove();
existingTray = true;
}
$pageWrapper.addClass('js-tray-open');
Drupal.offCanvas.visible = true;
$pageWrapper.append($offcanvasWrapper);
if (existingTray) {
Drupal.announce(Drupal.t('Configuration tray content has been updated.'));
} }
else { }
Drupal.announce(Drupal.t('Configuration tray opened.'));
$(window).on({
'dialog:aftercreate': function (event, dialog, $element, settings) {
if ($element.is('#drupal-offcanvas')) {
var eventData = {settings: settings, $element: $element};
$('.ui-dialog-offcanvas, .ui-dialog-offcanvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
$element
.on('dialogresize.outsidein', eventData, debounce(bodyPadding, 100))
.on('dialogContentResize.outsidein', eventData, handleDialogResize)
.trigger('dialogresize.outsidein');
$element.dialog('widget').attr('data-offset-' + edge, '');
$(window)
.on('resize.outsidein scroll.outsidein', eventData, debounce(resetSize, 100))
.trigger('resize.outsidein');
}
},
'dialog:beforecreate': function (event, dialog, $element, settings) {
if ($element.is('#drupal-offcanvas')) {
// @see http://api.jqueryui.com/position/
settings.position = {
my: 'left top',
at: edge + ' top',
of: window
};
settings.dialogClass = 'ui-dialog-offcanvas';
}
},
'dialog:beforeclose': function (event, dialog, $element) {
if ($element.is('#drupal-offcanvas')) {
$(document).off('.outsidein');
$(window).off('.outsidein');
$('body').css('padding-' + edge, 0);
}
} }
Drupal.attachBehaviors(document.querySelector('#offcanvas'), drupalSettings); });
};
})(jQuery, Drupal); })(jQuery, Drupal, Drupal.debounce, Drupal.displace);
...@@ -26,10 +26,10 @@ ...@@ -26,10 +26,10 @@
if (!localStorage.getItem('Drupal.contextualToolbar.isViewing')) { if (!localStorage.getItem('Drupal.contextualToolbar.isViewing')) {
return; return;
} }
var editLink = $(e.target).find('li.outside-inblock-configure a')[0]; var editLink = $(e.target).find('a[data-dialog-renderer="offcanvas"]')[0];
if (!editLink) { if (!editLink) {
var closest = $(e.target).closest('.outside-in-editable'); var closest = $(e.target).closest('.outside-in-editable');
editLink = closest.find('li.outside-inblock-configure a')[0]; editLink = closest.find('li a[data-dialog-renderer="offcanvas"]')[0];
} }
editLink.click(); editLink.click();
}); });
...@@ -48,25 +48,7 @@ ...@@ -48,25 +48,7 @@
// Bind Ajax behaviors to all items showing the class. // Bind Ajax behaviors to all items showing the class.
// @todo Fix contextual links to work with use-ajax links in // @todo Fix contextual links to work with use-ajax links in
// https://www.drupal.org/node/2764931. // https://www.drupal.org/node/2764931.
data.$el.find('.use-ajax').once('ajax').each(function () { Drupal.attachBehaviors(data.$el[0]);
// Below is copied directly from ajax.js to keep behavior the same.
var element_settings = {};
// Clicked links look better with the throbber than the progress bar.
element_settings.progress = {type: 'throbber'};
// For anchor tags, these will go to the target of the anchor rather
// than the usual location.
var href = $(this).attr('href');
if (href) {
element_settings.url = href;
element_settings.event = 'click';
}
element_settings.dialogType = $(this).data('dialog-type');
element_settings.dialog = $(this).data('dialog-options');
element_settings.base = $(this).attr('id');
element_settings.element = this;
Drupal.ajax(element_settings);
});
// Bind a listener to all 'Quick edit' links for blocks // Bind a listener to all 'Quick edit' links for blocks
// Click "Edit" button in toolbar to force Contextual Edit which starts // Click "Edit" button in toolbar to force Contextual Edit which starts
...@@ -141,6 +123,27 @@ ...@@ -141,6 +123,27 @@
$('.contextual-toolbar-tab.toolbar-tab button').on('click', function () { $('.contextual-toolbar-tab.toolbar-tab button').on('click', function () {
setToggleActiveMode(); setToggleActiveMode();
}); });
var search = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog';
var replace = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog_offcanvas';
// Loop through all Ajax links and change the format to offcanvas when
// needed.
Drupal.ajax.instances
.filter(function (instance) {
var hasElement = instance && !!instance.element;
var rendererOffcanvas = false;
var wrapperOffcanvas = false;
if (hasElement) {
rendererOffcanvas = $(instance.element).attr('data-dialog-renderer') === 'offcanvas';
wrapperOffcanvas = instance.options.url.indexOf('drupal_dialog_offcanvas') === -1;
}
return hasElement && rendererOffcanvas && wrapperOffcanvas;
})
.forEach(function (instance) {
// @todo Move logic for data-dialog-renderer attribute into ajax.js
// https://www.drupal.org/node/2784443
instance.options.url = instance.options.url.replace(search, replace);
});
} }
}; };
......
...@@ -10,6 +10,7 @@ drupal.outside_in: ...@@ -10,6 +10,7 @@ drupal.outside_in:
dependencies: dependencies:
- core/jquery - core/jquery
- core/drupal - core/drupal
- core/drupal.ajax
drupal.off_canvas: drupal.off_canvas:
version: VERSION version: VERSION
js: js:
......
...@@ -32,7 +32,8 @@ function outside_in_contextual_links_view_alter(&$element, $items) { ...@@ -32,7 +32,8 @@ function outside_in_contextual_links_view_alter(&$element, $items) {
if (isset($element['#links']['outside-inblock-configure'])) { if (isset($element['#links']['outside-inblock-configure'])) {
$element['#links']['outside-inblock-configure']['attributes'] = [ $element['#links']['outside-inblock-configure']['attributes'] = [
'class' => ['use-ajax'], 'class' => ['use-ajax'],
'data-dialog-type' => 'offcanvas', 'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'offcanvas',
]; ];
$element['#attached']['library'][] = 'outside_in/drupal.off_canvas'; $element['#attached']['library'][] = 'outside_in/drupal.off_canvas';
......
...@@ -3,7 +3,7 @@ services: ...@@ -3,7 +3,7 @@ services:
class: Drupal\outside_in\Render\MainContent\OffCanvasRender class: Drupal\outside_in\Render\MainContent\OffCanvasRender
arguments: ['@title_resolver', '@renderer'] arguments: ['@title_resolver', '@renderer']
tags: tags:
- { name: render.main_content_renderer, format: drupal_offcanvas } - { name: render.main_content_renderer, format: drupal_dialog_offcanvas }
outside_in.manager: outside_in.manager:
class: Drupal\outside_in\OutsideInManager class: Drupal\outside_in\OutsideInManager
......
...@@ -31,18 +31,21 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand { ...@@ -31,18 +31,21 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand {
* populated automatically from the current request. * populated automatically from the current request.
*/ */
public function __construct($title, $content, array $dialog_options = [], $settings = NULL) { public function __construct($title, $content, array $dialog_options = [], $settings = NULL) {
$dialog_options['modal'] = FALSE;