Commit 3efe9ba1 authored by catch's avatar catch

Issue #1851414 by nod_, quicksketch, frega, dawehner, damiankloip: Convert...

Issue #1851414 by nod_, quicksketch, frega, dawehner, damiankloip: Convert Views to use the abstracted dialog modal.
parent 8e086502
......@@ -5,15 +5,9 @@
/**
* The collapsible details object represents a single collapsible details element.
*/
function CollapsibleDetails(node, settings) {
function CollapsibleDetails (node) {
this.$node = $(node);
this.$node.data('details', this);
this.settings = $.extend({
duration:'fast',
easing:'linear'
},
settings
);
// Expand details if there are errors inside, or if it contains an
// element that is targeted by the URI fragment identifier.
var anchor = location.hash && location.hash !== '#' ? ', ' + location.hash : '';
......@@ -40,10 +34,6 @@ $.extend(CollapsibleDetails, {
* Extend CollapsibleDetails prototype.
*/
$.extend(CollapsibleDetails.prototype, {
/**
* Flag preventing multiple simultaneous animations.
*/
animating: false,
/**
* Initialize and setup summary events and markup.
*/
......@@ -77,8 +67,8 @@ $.extend(CollapsibleDetails.prototype, {
* Handle legend clicks
*/
onLegendClick: function (e) {
e.preventDefault();
this.toggle();
e.preventDefault();
},
/**
* Update summary
......@@ -91,59 +81,27 @@ $.extend(CollapsibleDetails.prototype, {
* Toggle the visibility of a details element using smooth animations.
*/
toggle: function () {
// Don't animate multiple times.
if (this.animating) {
return;
}
if (!this.$node.attr('open')) {
var $content = this.$node.find('> .details-wrapper').hide();
this.$node
.trigger({ type:'collapsed', value:false })
.find('> summary span.details-summary-prefix').html(Drupal.t('Hide'));
$content.slideDown(
$.extend(this.settings, {
complete:$.proxy(this.onCompleteSlideDown, this)
})
);
var isOpen = !!this.$node.attr('open');
var $summaryPrefix = this.$node.find('> summary span.details-summary-prefix');
if (isOpen) {
$summaryPrefix.html(Drupal.t('Show'));
}
else {
this.$node.trigger({ type:'collapsed', value:true });
this.$node.find('> .details-wrapper').slideUp(
$.extend(this.settings, {
complete:$.proxy(this.onCompleteSlideUp, this)
})
);
$summaryPrefix.html(Drupal.t('Hide'));
}
},
/**
* Completed opening details element.
*/
onCompleteSlideDown: function () {
this.$node.attr('open', true);
this.$node.trigger('completeSlideDown');
this.animating = false;
},
/**
* Completed closing details element.
*/
onCompleteSlideUp: function () {
this.$node.attr('open', false);
this.$node
.find('> summary span.details-summary-prefix').html(Drupal.t('Show'));
this.$node.trigger('completeSlideUp');
this.animating = false;
this.$node.attr('open', !isOpen);
}
});
Drupal.behaviors.collapse = {
attach: function (context, settings) {
attach: function (context) {
if (Modernizr.details) {
return;
}
var $collapsibleDetails = $(context).find('details').once('collapse');
if ($collapsibleDetails.length) {
for (var i = 0; i < $collapsibleDetails.length; i++) {
CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i], settings.collapsibleDetails));
CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i]));
}
}
}
......
......@@ -24,9 +24,10 @@
if ($dialog.length) {
// Remove and replace the dialog buttons with those from the new form.
if ($dialog.dialog('option', 'drupalAutoButtons')) {
var buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
$dialog.dialog('option', 'buttons', buttons);
// Trigger an event to detect/sync changes to buttons.
$dialog.trigger('dialogButtonsChange');
}
// Refocus the first input element after validation errors.
if ($context.find('form').length) {
$context.find('input:first').focus();
......@@ -61,7 +62,7 @@
'text': $originalButton.html() || $originalButton.attr('value'),
'class': $originalButton.attr('class'),
'click': function (e) {
$originalButton.trigger('click');
$originalButton.trigger('mousedown').trigger('click').trigger('mouseup');
e.preventDefault();
}
});
......@@ -98,6 +99,12 @@
response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
}
// Bind dialogButtonsChange
$dialog.on('dialogButtonsChange', function() {
var buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
$dialog.dialog('option', 'buttons', buttons);
});
// Open the dialog itself.
response.dialogOptions = response.dialogOptions || {};
var dialog = Drupal.dialog($dialog, response.dialogOptions);
......@@ -107,6 +114,9 @@
else {
dialog.show();
}
// Add the standard Drupal class for buttons for style consistency.
$dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
};
/**
......@@ -122,6 +132,9 @@
$dialog.remove();
}
}
// Unbind dialogButtonsChange
$dialog.off('dialogButtonsChange');
};
/**
......
......@@ -10,9 +10,6 @@
drupalSettings.dialog = {
autoOpen: true,
// This option will turn off resizable and draggable.
autoResize: true,
maxHeight: '95%',
dialogClass: '',
// When using this API directly (when generating dialogs on the client side),
// you may want to override this method and do
......@@ -32,14 +29,6 @@ Drupal.dialog = function (element, options) {
// Trigger a global event to allow scripts to bind events to the dialog.
$(window).trigger('dialog:beforecreate', [dialog, $element, settings]);
$element.dialog(settings);
if (settings.autoResize === true || settings.autoResize === 'true') {
$element
.dialog('option', { resizable: false, draggable: false })
.dialog('widget').css('position', 'fixed');
$(window)
.on('resize.dialogResize scroll.dialogResize', settings, autoResize)
.trigger('resize.dialogResize');
}
dialog.open = true;
$(window).trigger('dialog:aftercreate', [dialog, $element, settings]);
}
......@@ -49,44 +38,11 @@ Drupal.dialog = function (element, options) {
$element.dialog('close');
dialog.returnValue = value;
dialog.open = false;
$(window).off('.dialogResize');
$(window).trigger('dialog:afterclose', [dialog, $element]);
}
/**
* Resets the current options for positioning.
*
* This is used as a window resize and scroll callback to reposition the
* jQuery UI dialog. Although not a built-in jQuery UI option, this can
* be disabled by setting autoResize: false in the options array when creating
* a new Drupal.dialog().
*/
function resetPosition (event) {
var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position'];
var windowHeight = $(window).height();
var adjustedOptions = {};
var option, optionValue, adjustedValue;
for (var n = 0; n < positionOptions.length; n++) {
option = positionOptions[n];
optionValue = event.data[option];
if (optionValue) {
// jQuery UI does not support percentages on heights, convert to pixels.
if (typeof optionValue === 'string' && /%$/.test(optionValue) && /height/i.test(option)) {
adjustedValue = parseInt(0.01 * parseInt(optionValue, 10) * windowHeight, 10);
// Don't force the dialog to be bigger vertically than needed.
if (option === 'height' && $element.parent().outerHeight() < adjustedValue) {
adjustedValue = 'auto';
}
adjustedOptions[option] = adjustedValue;
}
}
}
$element.dialog('option', adjustedOptions);
}
var undef;
var $element = $(element);
var autoResize = Drupal.debounce(resetPosition, 50);
var dialog = {
open: false,
returnValue: undef,
......
(function ($, Drupal, drupalSettings, debounce, displace) {
"use strict";
// autoResize option will turn off resizable and draggable.
drupalSettings.dialog = $.extend({ autoResize: true, maxHeight: '95%' }, drupalSettings.dialog);
/**
* Resets the current options for positioning.
*
* This is used as a window resize and scroll callback to reposition the
* jQuery UI dialog. Although not a built-in jQuery UI option, this can
* be disabled by setting autoResize: false in the options array when creating
* a new Drupal.dialog().
*/
function resetSize (event) {
var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position'];
var adjustedOptions = {};
var windowHeight = $(window).height();
var option, optionValue, adjustedValue;
for (var n = 0; n < positionOptions.length; n++) {
option = positionOptions[n];
optionValue = event.data.settings[option];
if (optionValue) {
// jQuery UI does not support percentages on heights, convert to pixels.
if (typeof optionValue === 'string' && /%$/.test(optionValue) && /height/i.test(option)) {
// Take offsets in account.
windowHeight -= displace.offsets.top + displace.offsets.bottom;
adjustedValue = parseInt(0.01 * parseInt(optionValue, 10) * windowHeight, 10);
// Don't force the dialog to be bigger vertically than needed.
if (option === 'height' && event.data.$element.parent().outerHeight() < adjustedValue) {
adjustedValue = 'auto';
}
adjustedOptions[option] = adjustedValue;
}
}
}
// Offset the dialog center to be at the center of Drupal.displace.offsets.
adjustedOptions = resetPosition(adjustedOptions);
event.data.$element
.dialog('option', adjustedOptions)
.trigger('dialogContentResize');
}
/**
* Position the dialog's center at the center of displace.offsets boundaries.
*/
function resetPosition (options) {
var offsets = displace.offsets;
var left = offsets.left - offsets.right;
var top = offsets.top - offsets.bottom;
var leftString = (left > 0 ? '+' : '-') + Math.abs(Math.round(left/2)) + 'px';
var topString = (top > 0 ? '+' : '-') + Math.abs(Math.round(top/2)) + 'px';
options.position = {
my: 'center' + (left !== 0 ? leftString : '') + ' center' + (top !== 0 ? topString : '')
};
return options;
}
$(window).on({
'dialog:aftercreate': function (event, dialog, $element, settings) {
var autoResize = debounce(resetSize, 20);
var eventData = { settings: settings, $element: $element };
if (settings.autoResize === true || settings.autoResize === 'true') {
$element
.dialog('option', { resizable: false, draggable: false })
.dialog('widget').css('position', 'fixed');
$(window)
.on('resize.dialogResize scroll.dialogResize', eventData, autoResize)
.trigger('resize.dialogResize');
$(document).on('drupalViewportOffsetChange', eventData, autoResize);
}
},
'dialog:beforeclose': function (event, dialog, $element) {
$(window).off('.dialogResize');
}
});
// @todo remove window.parent once overlay is removed from core.
})(jQuery, Drupal, drupalSettings, Drupal.debounce, window.parent.Drupal.displace);
......@@ -38,43 +38,6 @@
padding: 0;
}
.ui-dialog .ui-dialog-buttonpane button {
background: #fefefe;
background-image: -webkit-linear-gradient(top, #fefefe, #e0e0e0);
background-image: -moz-linear-gradient(top, #fefefe, #e0e0e0);
background-image: -o-linear-gradient(top, #fefefe, #e0e0e0);
background-image: linear-gradient(to bottom, #fefefe, #e0e0e0);
border: 1px solid #c8c8c8;
border-radius: 3px;
text-decoration: none;
padding: 6px 17px 6px 17px;
}
.ui-dialog .ui-dialog-buttonpane button:hover,
.ui-dialog .ui-dialog-buttonpane button:focus {
background: #fefefe;
background-image: -webkit-linear-gradient(top, #fefefe, #eaeaea);
background-image: -moz-linear-gradient(top, #fefefe, #eaeaea);
background-image: -o-linear-gradient(top, #fefefe, #eaeaea);
background-image: linear-gradient(to bottom, #fefefe, #eaeaea);
-webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
color: #2e2e2e;
text-decoration: none;
}
.ui-dialog .ui-dialog-buttonpane button:active {
border: 1px solid #c8c8c8;
background: #fefefe;
background-image: -webkit-linear-gradient(top, #eaeaea, #fefefe);
background-image: -moz-linear-gradient(top, #eaeaea, #fefefe);
background-image: -o-linear-gradient(top, #eaeaea, #fefefe);
background-image: linear-gradient(to bottom, #eaeaea, #fefefe);
-webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
color: #2e2e2e;
text-decoration: none;
text-shadow: none;
}
/* Form action buttons are moved in dialogs. Remove empty space. */
.ui-dialog .ui-dialog-content .form-actions {
padding: 0;
......
......@@ -419,7 +419,7 @@ public function buildOptionsForm(&$form, &$form_state) {
'#options' => $formatters,
'#default_value' => $this->options['type'],
'#ajax' => array(
'path' => views_ui_build_form_url($form_state),
'path' => views_ui_build_form_path($form_state),
),
'#submit' => array(array($this, 'submitTemporaryForm')),
'#executes_submit_callback' => TRUE,
......
......@@ -1079,6 +1079,7 @@ function system_library_info() {
'version' => \Drupal::VERSION,
'js' => array(
'core/misc/dialog.js' => array('group' => JS_LIBRARY),
'core/misc/dialog.position.js' => array('group' => JS_LIBRARY),
),
'css' => array(
'core/misc/dialog.theme.css' => array('weight' => 1),
......@@ -1086,9 +1087,10 @@ function system_library_info() {
'dependencies' => array(
array('system', 'jquery'),
array('system', 'drupal'),
array('system', 'drupal.debounce'),
array('system', 'drupalSettings'),
array('system', 'jquery.ui.dialog')
array('system', 'drupal.debounce'),
array('system', 'drupal.displace'),
array('system', 'jquery.ui.dialog'),
),
);
......
......@@ -6,7 +6,7 @@
*/
use Drupal\views\Ajax\HighlightCommand;
use Drupal\views\Ajax\SetFormCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\AjaxResponse;
/**
......@@ -52,10 +52,12 @@ function views_ajax_form_wrapper($form_id, &$form_state) {
$display .= $output;
$title = empty($form_state['title']) ? '' : $form_state['title'];
$options = array(
'dialogClass' => 'views-ui-dialog',
'width' => '50%',
);
$url = empty($form_state['url']) ? url(current_path(), array('absolute' => TRUE)) : $form_state['url'];
$response->addCommand(new SetFormCommand($display, $title, $url));
$response->addCommand(new OpenModalDialogCommand($title, $display, $options));
if (!empty($form_state['#section'])) {
$response->addCommand(new HighlightCommand('.' . drupal_clean_css_identifier($form_state['#section'])));
......
/**
* This is part of a patch to address a jQueryUI bug. The bug is responsible
* for the inability to scroll a page when a modal dialog is active. If the content
* of the dialog extends beyond the bottom of the viewport, the user is only able
* to scroll with a mousewheel or up/down keyboard keys.
*
* @see http://bugs.jqueryui.com/ticket/4671
* @see https://bugs.webkit.org/show_bug.cgi?id=19033
* @see views_ui.module
* @see js/jquery.ui.dialog.min.js
*
* This javascript patch overwrites the $.ui.dialog.overlay.events object to remove
* the mousedown, mouseup and click events from the list of events that are bound
* in $.ui.dialog.overlay.create
*
* The original code for this object:
* $.ui.dialog.overlay.events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
* function(event) { return event + '.dialog-overlay'; }).join(' '),
*
*/
(function ($, undefined) {
"use strict";
if ($.ui && $.ui.dialog) {
$.ui.dialog.overlay.events = $.map('focus,keydown,keypress'.split(','),
function(event) { return event + '.dialog-overlay'; }).join(' ');
}
}(jQuery));
<?php
/**
* @file
* Contains \Drupal\views\Ajax\DismissFormCommand.
*/
namespace Drupal\views\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* Provides an AJAX command for closing the views form modal.
*
* This command is implemented in Drupal.AjaxCommands.prototype.viewsDismissForm.
*/
class DismissFormCommand implements CommandInterface {
/**
* Implements \Drupal\Core\Ajax\CommandInterface::render().
*/
public function render() {
return array(
'command' => 'viewsDismissForm',
);
}
}
<?php
/**
* @file
* Contains \Drupal\views\Ajax\SetFormCommand.
*/
namespace Drupal\views\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* Provides an AJAX command for setting a form in the views edit modal.
*
* This command is implemented in Drupal.AjaxCommands.prototype.viewsSetForm.
*/
class SetFormCommand implements CommandInterface {
/**
* The rendered output of the form.
*
* @var string
*/
protected $output;
/**
* The title of the form.
*
* @var string
*/
protected $title;
/**
* The URL of the form.
*
* @var string
*/
protected $url;
/**
* Constructs a \Drupal\views\Ajax\ReplaceTitleCommand object.
*
* @param string $output
* The form to display in the modal.
* @param string $title
* The title of the form.
* @param string $url
* (optional) An optional URL of the form.
*/
public function __construct($output, $title, $url = NULL) {
$this->output = $output;
$this->title = $title;
$this->url = $url;
}
/**
* Implements \Drupal\Core\Ajax\CommandInterface::render().
*/
public function render() {
$command = array(
'command' => 'viewsSetForm',
'output' => $this->output,
'title' => $this->title,
);
if (isset($this->url)) {
$command['url'] = $this->url;
}
return $command;
}
}
......@@ -352,7 +352,7 @@ function views_ui_standard_display_dropdown(&$form, &$form_state, $section) {
// @todo Move this to a separate function if it's needed on any forms that
// don't have the display dropdown.
$form['override'] = array(
'#prefix' => '<div class="views-override clearfix container-inline">',
'#prefix' => '<div class="views-override clearfix container-inline" data-drupal-views-offset="top">',
'#suffix' => '</div>',
'#weight' => -1000,
'#tree' => TRUE,
......@@ -402,20 +402,20 @@ function views_ui_standard_display_dropdown(&$form, &$form_state, $section) {
}
/**
* Create the URL for one of our standard AJAX forms based upon known
* Create the menu path for one of our standard AJAX forms based upon known
* information about the form.
*/
function views_ui_build_form_url($form_state) {
function views_ui_build_form_path($form_state) {
$ajax = empty($form_state['ajax']) ? 'nojs' : 'ajax';
$name = $form_state['view']->id();
$url = "admin/structure/views/$ajax/$form_state[form_key]/$name/$form_state[display_id]";
$path = "admin/structure/views/$ajax/$form_state[form_key]/$name/$form_state[display_id]";
if (isset($form_state['type'])) {
$url .= '/' . $form_state['type'];
$path .= '/' . $form_state['type'];
}
if (isset($form_state['id'])) {
$url .= '/' . $form_state['id'];
$path .= '/' . $form_state['id'];
}
return $url;
return $path;
}
/**
......
......@@ -209,35 +209,6 @@
/* @end */
/* @group Modal dialog box */
.views-ui-dialog {
/* We need this so the button is visible. */
overflow: visible;
position: fixed;
}
.views-ui-dialog .ui-dialog-titlebar-close {
border: 1px solid transparent;
display: block;
margin: 0;
padding: 0;
position: absolute;
right: 0;
top: 2px;
/* Make sure this is in front of the modal backdrop. */
z-index: 1002;
}
.views-ui-dialog .ui-dialog-titlebar {
padding: 0;
margin: 0;
}
.views-ui-dialog .ui-dialog-title {
display: none;
}
.views-ui-dialog #views-ajax-popup {
padding: 0;
overflow: hidden;
......@@ -253,8 +224,8 @@
}
.views-ui-dialog .scroll {
max-height: 400px;
overflow: auto;
padding: 1em;
}
#views-filterable-options-controls {
......
......@@ -401,15 +401,6 @@ td.group-title {
padding-right: 10px;
}
/* This keeps the collapsible details of options from crashing into the bottom
* of the edit option columns. Because the edit option columns are floated, the collapsible
* details need to be floated as well so that the margin above the details interacts with
* the float edit option columns.
*/
#edit-options details {
width: 100%;
}
#edit-options-more {
clear: both;
}
......@@ -783,28 +774,6 @@ ul#views-display-menu-tabs li.add ul.action-list li{
* The contents of the popup dialog on the views edit form.
*/
.views-ui-dialog {
font-size: small;
padding: 0;
}
.views-ui-dialog .ui-dialog-titlebar-close {
background: url("../images/close.png") no-repeat scroll 6px 3px #f3f4ee;
border-color: #aaa;
border-radius: 0 10px 12px 0;
border-style: solid;
border-width: 1px 1px 1px 0;
box-shadow: 0 -2px 0 rgba(0, 0, 0, 0.1);
height: 22px;
right: -28px;
top: 0;
width: 26px;
}
.views-ui-dialog .ui-dialog-titlebar-close span {
display: none;
}
.views-filterable-options .form-type-checkbox {
border: 1px solid #ccc;
padding: 5px 8px;
......@@ -847,6 +816,10 @@ ul#views-display-menu-tabs li.add ul.action-list li{
width: 200px;
}
.views-ui-dialog .ui-dialog-content {
padding: 0;
}
.views-ui-dialog .views-filterable-options {
margin-bottom: 10px;
}
......@@ -869,8 +842,24 @@ ul#views-display-menu-tabs li.add ul.action-list li{
background-color: #f3f4ee;
}
.views-ui-dialog.views-ui-dialog-scroll .ui-dialog-titlebar {
border: none;
}
.views-ui-dialog .views-override {
padding: 0 13px 8px;
padding: 8px 13px;
}
.views-ui-dialog [data-drupal-views-offset] {
border: 1px solid #ccc;
}
.views-ui-dialog [data-drupal-views-offset="top"] {
border-width: 0 0 1px;
}
.views-ui-dialog [data-drupal-views-offset="bottom"] {
border-width: 1px 0 0;
}
.views-ui-dialog .views-override > * {
......@@ -902,25 +891,9 @@ ul#views-display-menu-tabs li.add ul.action-list li{
content: "\00A0\003E";
}