diff --git a/core/misc/edit.png b/core/misc/edit.png new file mode 100644 index 0000000..d176245 --- /dev/null +++ b/core/misc/edit.png @@ -0,0 +1,3 @@ +PNG + + IHDR!-#IDATxڵ́@EєKH)%XHDDĀ(Fl,ᗐۻ0BI>80LҿfPԠȾjpŰG.E4־kdpa:Ǐǣ79Bp08OTYv;C n8!C fcp(#=^Uu%_V9T{l\Vy/X, E{n>Zwf5x@EQ  t:C[L&9jȳ/$UW5IENDB` \ No newline at end of file diff --git a/core/modules/contextual/contextual.base-rtl.css b/core/modules/contextual/contextual.base-rtl.css deleted file mode 100644 index 147a567..0000000 --- a/core/modules/contextual/contextual.base-rtl.css +++ /dev/null @@ -1,9 +0,0 @@ - -/** - * @file - * RTL base styles for the Contextual module. - */ - -.contextual .trigger { - text-align: left; -} diff --git a/core/modules/contextual/contextual.base.css b/core/modules/contextual/contextual.base.css index cbec804..8452f52 100644 --- a/core/modules/contextual/contextual.base.css +++ b/core/modules/contextual/contextual.base.css @@ -4,35 +4,36 @@ * Generic base styles for contextual module. */ -/** - * Contextual links behavior. - */ -.contextual, -.contextual .contextual-links, -.contextual .trigger { +.contextual-region { + position: relative; +} +.touch .contextual .trigger { + display: block; +} +.contextual .contextual-links { display: none; } -.touch .contextual, -.touch .contextual .trigger, -.no-touch .contextual-region:hover .contextual, -.no-touch .contextual-region:hover .contextual-links-trigger-active, -.contextual-active .contextual-links { +.contextual-links-active .contextual-links { display: block; } /** - * Contextual links structure. + * The .element-focusable class extends the .element-invisible class to allow + * the element to be focusable when navigated to via the keyboard. + * + * Add support for hover. */ -.contextual-region { - position: relative; +.touch .contextual-region .element-invisible.element-focusable, +.contextual-region:hover .element-invisible.element-focusable { + clip: auto; + overflow: visible; + height: auto; } -.contextual { - position: absolute; - z-index: 999; -} -.contextual .trigger { - overflow: hidden; - position: relative; - text-align: right; /* LTR */ - z-index: 1; +/* Override the position for contextual links. */ +.contextual-region .element-invisible.element-focusable:active, +.contextual-region .element-invisible.element-focusable:focus, +.contextual-region:hover .element-invisible.element-focusable, +.contextual-region-active .element-invisible.element-focusable, +.touch .contextual-region .element-invisible.element-focusable { + position: relative !important; } diff --git a/core/modules/contextual/contextual.js b/core/modules/contextual/contextual.js index d8a4742..0759515 100644 --- a/core/modules/contextual/contextual.js +++ b/core/modules/contextual/contextual.js @@ -3,55 +3,177 @@ * Attaches behaviors for the Contextual module. */ -(function ($) { +(function ($, Drupal) { "use strict"; -Drupal.contextualLinks = Drupal.contextualLinks || {}; +var contextuals = []; /** * Attaches outline behavior for regions associated with contextual links. */ -Drupal.behaviors.contextualLinks = { +Drupal.behaviors.contextual = { attach: function (context) { - $(context).find('div.contextual').once('contextual-links', function () { - var $wrapper = $(this); - var $region = $wrapper.closest('.contextual-region'); - var $links = $wrapper.find('ul'); - var $trigger = $('').text(Drupal.t('Configure')).click( - function (e) { - e.preventDefault(); - e.stopPropagation(); - $links.stop(true, true).slideToggle(100); - $wrapper.toggleClass('contextual-active'); - } - ); - // Attach hover behavior to trigger and ul.contextual-links, for non touch devices only. - if(!Modernizr.touch) { - $trigger.add($links).hover( - function () { $region.addClass('contextual-region-active'); }, - function () { $region.removeClass('contextual-region-active'); } - ); - } - // Hide the contextual links when user clicks a link or rolls out of the .contextual-region. - $region.bind('mouseleave click', Drupal.contextualLinks.mouseleave); - $region.hover( - function() { $trigger.addClass('contextual-links-trigger-active'); }, - function() { $trigger.removeClass('contextual-links-trigger-active'); } - ); - // Prepend the trigger. - $wrapper.prepend($trigger); + $('ul.contextual-links', context).once('contextual', function () { + var $this = $(this); + var contextual = new Drupal.contextual(this, $this.closest('.contextual-region')); + contextuals.push(contextual); + $this.data('drupal-contextual', contextual); }); + // Bind to global edit mode changes + $('body').once('contextual', function (index, element) { + $(document) + .on('drupalEditMode.contextual', toggleEditMode); + }); + } +}; + +/** + * Contextual links object. + */ +Drupal.contextual = function($links, $region) { + this.$links = $links; + this.$region = $region; + this.timer = null; + + this.init(); +}; + +/** + * Initiates a contextual links object. + */ +Drupal.contextual.prototype.init = function() { + // Wrap the links to provide positioning and behavior attachment context. + this.$wrapper = $(Drupal.theme.contextualWrapper()) + .insertBefore(this.$links) + .append(this.$links); + + // Create and append the contextual links trigger. + var action = Drupal.t('Open'); + this.$trigger = $(Drupal.theme.contextualTrigger()) + .text(Drupal.t('@action configuration options', {'@action': action})) + .prependTo(this.$wrapper); + + // The trigger behaviors are never detached or mutated. + this.$region + .on('click.contextual', '.contextual .trigger', $.proxy(this.triggerClickHandler, this)) + .on('mouseleave.contextual', '.contextual', {show: false}, $.proxy(this.triggerLeaveHandler, this)) + // Attach highlight behaviors. + this.attachHighlightBehaviors(); +}; + +/** + * + */ +Drupal.contextual.prototype.attachHighlightBehaviors = function () { + // Bind behaviors through delegation. + var highlightRegion = $.proxy(this.highlightRegion, this); + this.$region + .on('mouseenter.contextual.highlight', {highlight: true}, highlightRegion) + .on('mouseleave.contextual.highlight', {highlight: false}, highlightRegion) + .on('focus.contextual.highlight', '.contextual-links a, .contextual .trigger', {highlight: true}, highlightRegion) + .on('blur.contextual.highlight', '.contextual-links a, .contextual .trigger', {highlight: false}, highlightRegion); +}; + +/** + * + */ +Drupal.contextual.prototype.detachHighlightBehaviors = function () { + this.$region.off('.contextual.highlight'); +}; + +/** + * Toggles the highlighting of a contextual region. + * + * If the state of a contextual region is toggled to inactive, the links + * + * @param Object event + * jQuery Event object. + */ +Drupal.contextual.prototype.highlightRegion = function(event) { + // Set up a timeout to delay the dismissal of the region highlight state. + if (!event.data.highlight && !this.timer) { + return this.timer = window.setTimeout($.proxy($.fn.trigger, $(event.target), 'mouseleave.contextual'), 100); + } + // Clear the timeout if the region should be highlighted and a timer exists. + if (event.data.highlight && this.timer) { + window.clearTimeout(this.timer); } + this.$region.toggleClass('contextual-region-active', event.data.highlight); + // Hide the links if the contextual region is inactive. + var state = this.$region.hasClass('contextual-region-active'); + if (!state) { + this.showLinks(state); + } + // Clear the timeout. + this.timer = null; +}; + +/** + * Handles click on the contextual links trigger. + * + * @param Object event + * jQuery Event object. + */ +Drupal.contextual.prototype.triggerClickHandler = function (event) { + event.preventDefault(); + this.showLinks(); +}; + +/** + * Handles mouseleave on the contextual links trigger. + * + * @param Object event + * jQuery Event object. + */ +Drupal.contextual.prototype.triggerLeaveHandler = function (event) { + var show = event && event.data && event.data.show; + this.showLinks(show); +}; + +/** + * Toggles the active state of the contextual links. + * + * @param Boolean show + * (optional) True if the links should be shown. False is the links should be + * hidden. + */ +Drupal.contextual.prototype.showLinks = function(show) { + this.$wrapper.toggleClass('contextual-links-active', show); + var isOpen = this.$wrapper.hasClass('contextual-links-active'); + var action = (isOpen) ? Drupal.t('Close') : Drupal.t('Open'); + this.$trigger + .text(Drupal.t('@action configuration options', {'@action': action})); +}; + +/** + * + */ +function toggleEditMode (event, data) { + for (var i = contextuals.length - 1; i >= 0; i--) { + contextuals[i][(data.editable) ? 'detachHighlightBehaviors' : 'attachHighlightBehaviors'](); + contextuals[i].$region.toggleClass('contextual-region-active', data.editable); + }; +} + +/** + * Wraps contextual links. + * + * @return {String} + * A string representing a DOM fragment. + */ +Drupal.theme.contextualWrapper = function () { + return '
'; }; /** - * Disables outline for the region contextual links are associated with. + * A trigger is an interactive element often bound to a click handler. + * + * @return {String} + * A string representing a DOM fragment. */ -Drupal.contextualLinks.mouseleave = function () { - $(this) - .find('.contextual-active').removeClass('contextual-active') - .find('.contextual-links').hide(); +Drupal.theme.contextualTrigger = function () { + return ''; }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index bbb00e8..30dcacb 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -69,8 +69,6 @@ function contextual_element_info() { '#pre_render' => array('contextual_pre_render_links'), '#theme' => 'links__contextual', '#links' => array(), - '#prefix' => '
', - '#suffix' => '
', '#attributes' => array('class' => array('contextual-links')), '#attached' => array( 'library' => array( diff --git a/core/modules/contextual/contextual.theme-rtl.css b/core/modules/contextual/contextual.theme-rtl.css index f558ffa..ed48367 100644 --- a/core/modules/contextual/contextual.theme-rtl.css +++ b/core/modules/contextual/contextual.theme-rtl.css @@ -3,17 +3,26 @@ * RTL styling for contextual module. */ +/** + * Contextual links wrappers. + */ .contextual { - left: 5px; - right: auto; -} -.contextual .contextual-links { - border-radius: 0 4px 4px 4px; left: 0; right: auto; } -.contextual-region .contextual .contextual-links a { - text-align: right; - padding: 0.4em 0.6em 0.4em 0.8em; +/** + * Contextual trigger. + */ +.contextual .trigger { + float: left; +} + +/** + * Contextual links. + */ +.contextual .contextual-links { + border-radius: 0 4px 4px 4px; + float: left; + text-align: right; } diff --git a/core/modules/contextual/contextual.theme.css b/core/modules/contextual/contextual.theme.css index 8b5956a..122234c 100644 --- a/core/modules/contextual/contextual.theme.css +++ b/core/modules/contextual/contextual.theme.css @@ -6,40 +6,42 @@ /** * Contextual links wrappers. */ -.contextual-region-active { - outline: 1px dashed #d6d6d6; - outline-offset: 1px; -} .contextual { - right: 2px; /* LTR */ + position: absolute; + right: 0; /* LTR */ top: 2px; + z-index: 999; +} +.contextual-region-active { + outline: 1px solid #007fff; + outline-offset: 1px; } /** * Contextual trigger. */ .contextual .trigger { - background: transparent url(images/gear-select.png) no-repeat 2px 0; - border: 1px solid transparent; - height: 18px; + background: #ffffff url("../../misc/edit.png") no-repeat center center; + background-size: 16px 16px; + border: 1px solid #ddd; + border-radius: 13px; + box-shadow:1px 1px 2px rgba(0,0,0,0.3); + /* Override the .element-focusable height: auto */ + height: 28px !important; + float: right; /* LTR */ margin: 0; - outline: none; overflow: hidden; padding: 0 2px; - text-indent: 34px; + position: relative; + right: 2px; width: 28px; + text-indent: -9999px; + z-index: 2; } -.no-touch .contextual .trigger:hover, -.contextual-active .trigger { - background-position: 2px -18px; -} -.contextual-active .trigger { - background-color: #ffffff; - border-bottom: none; - border-color: #d6d6d6; - border-radius: 4px 4px 0 0; - position: relative; - z-index: 1; +.contextual-links-active .trigger { + border-radius: 14px 14px 0 0; + border-bottom: 1px solid transparent; + box-shadow: 2px 0 0 rgba(0,0,0,0.15); } /** @@ -47,17 +49,21 @@ */ .contextual .contextual-links { background-color: #fff; - border: 1px solid #d6d6d6; - border-radius: 4px 0 4px 4px; /* LTR */ + border: 1px solid #ddd; + border-radius: 10px 0 10px 10px; /* LTR */ + box-shadow: 1px 1px 2px rgba(0,0,0,0.3); + clear: both; + float: right; /* LTR */ margin: 0; padding: 0.25em 0; - position: absolute; - right: 0; /* LTR */ - text-align: left; - top: 18px; + position: relative; + right: 2px; + text-align: left; /* LTR */ + top: -1px; white-space: nowrap; + z-index: 1; } -/* Reset the li to prevent accidential overrides by a theme. */ +/* Reset the li to prevent accidental overrides by a theme. */ .contextual-region .contextual .contextual-links li { background-color: #fff; border: none; @@ -65,14 +71,16 @@ list-style-image: none; margin: 0; padding: 0; + line-height: 100%; } .contextual-region .contextual .contextual-links a { + color: black !important; display: block; font-family: sans-serif; font-size: small; - line-height: 0.8em; + line-height: 1.8em; margin: 0.25em 0; - padding: 0.4em 0.8em 0.4em 0.6em; /* LTR */ + padding: 0.4em 0.6em; } .contextual-region .contextual .contextual-links a, .no-touch .contextual-region .contextual .contextual-links a:hover, @@ -83,5 +91,7 @@ text-decoration: none; } .no-touch .contextual-region .contextual .contextual-links li a:hover { - background-color: #bfdcee; + color: white; + background-image: -webkit-linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%); + background-image: linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%); } diff --git a/core/modules/edit/css/edit.css b/core/modules/edit/css/edit.css index 37e10eb..ce3d19e 100644 --- a/core/modules/edit/css/edit.css +++ b/core/modules/edit/css/edit.css @@ -1,4 +1,32 @@ /** + * Pencil icon. + */ +.edit-toolbar-container .pencil { + background: #fff url(../../../misc/edit.png) no-repeat center center; + background-size: 16px 16px; + border: 1px solid #ddd; + border-radius: 13px; + -moz-box-shadow: 1px 1px 2px rgba(0,0,0,0.3); + -webkit-box-shadow: 1px 1px 2px rgba(0,0,0,0.3); + box-shadow: 1px 1px 2px rgba(0,0,0,0.3); + height: 26px; + width: 26px; + margin: 0; + outline: none; + overflow: hidden; + padding:0; + text-indent: 34px; + position: absolute; + right: 2px; /* LTR */ + top: 2px; +} + +.edit-toolbar-container.edit-editing .pencil { + display: none; +} + + +/** * Animations. */ .edit-animate-invisible { @@ -75,10 +103,10 @@ * Toolbar. */ .icon-edit:before { - background-image: url("../images/icon-edit.png"); + background-image: url("../../../misc/edit.png"); } .icon-edit:active:before, -.active .icon-edit:before { +.active.icon-edit:before { background-image: url("../images/icon-edit-active.png"); } .toolbar .tray.edit.active { @@ -106,8 +134,6 @@ z-index: 250; width: 100%; height: 100%; - background-color: #fff; - background-color: rgba(255,255,255,.5); top: 0; left: 0; } @@ -122,11 +148,18 @@ } .edit-field.edit-editable, .edit-field .edit-editable { - box-shadow: 0 0 1px 1px #4d9de9; + /** + * In the latest design, there's no need to indicate candidates, since they + * now use pencil icons. + * This will probably be necessary again before release. + */ } /* Highlighted (hovered) editable. */ .edit-editable.edit-highlighted { + z-index: 305; +} +.edit-editable.edit-highlighted { min-width: 200px; } .edit-field.edit-editable.edit-highlighted, @@ -279,6 +312,10 @@ bottom: 1px; box-shadow: 0 0 1px 1px #0199ff, 0 0 3px 3px rgba(153, 153, 153, .5); background: #fff; + display: none; +} +.edit-highlighted .edit-toolbar-heightfaker { + display: block; } /* The toolbar; these are not necessarily visible. */ diff --git a/core/modules/edit/images/icon-edit-active.png b/core/modules/edit/images/icon-edit-active.png index ad84761..a9143aa 100644 --- a/core/modules/edit/images/icon-edit-active.png +++ b/core/modules/edit/images/icon-edit-active.png @@ -1,3 +1,8 @@ PNG  - IHDRj `PLTE[tRNS@ P00p`ϟDƙIDATxe DQ8Ϩ/BDU9xV+D\?x@qWcF8wicS B}?v;Vf.V$JgX=Kضp0XS"iRw\:LL\~;Z5wu 5E)LIENDB` \ No newline at end of file + IHDR!-IDATxڭmP]”@ )(%PGܕHX@ JH<(4p|O%}y Y~ +tu&0hA+\l'Sv'`^+yX +)DsqV+9%FaHrTCͦs"6 +sPa%"y{{Soր=!_\Ay}O`|l0GE+! &hdY0'/M`NR!C2Nh~CR˂sW9yiH. "D0G\Od2Q +R>Hq.APa`0.h߹#.~_az&.w&Ha*ѿ +⹆ M0Gia MHn0GCNsn+Cժ a"BO+}37IENDB` \ No newline at end of file diff --git a/core/modules/edit/images/icon-edit.png b/core/modules/edit/images/icon-edit.png deleted file mode 100644 index 4f0dcc2..0000000 --- a/core/modules/edit/images/icon-edit.png +++ /dev/null @@ -1,5 +0,0 @@ -PNG - - IHDRj PLTE̻ʪ̡̜ˠ̣̽¼ǷʨªZ(e+tRNSϟ `π@`0p0p`0PϟϟcIDATxeW0{W -H"ʵ,y {Hpyo?mf,RBRxB vL;&LPJaRb\(Tbn(1wϔJ)ԈkS -58äT^4P6c}[i <ާ'-+HP>KIENDB` \ No newline at end of file diff --git a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js index 5671f39..caac604 100644 --- a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js +++ b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js @@ -29,13 +29,6 @@ _initialize: function() { var that = this; - // Sets the state to 'activated' upon clicking the element. - this.element.on("click.edit", function(event) { - event.stopPropagation(); - event.preventDefault(); - that.options.activated(); - }); - // Sets the state to 'changed' whenever the content has changed. var before = jQuery.trim(this.element.text()); this.element.on('keyup paste', function (event) { @@ -68,6 +61,7 @@ case 'highlighted': break; case 'activating': + this.options.activated(); break; case 'active': // Sets the "contenteditable" attribute to "true". diff --git a/core/modules/edit/js/createjs/editingWidgets/formwidget.js b/core/modules/edit/js/createjs/editingWidgets/formwidget.js index 3238566..ac89857 100644 --- a/core/modules/edit/js/createjs/editingWidgets/formwidget.js +++ b/core/modules/edit/js/createjs/editingWidgets/formwidget.js @@ -29,15 +29,7 @@ /** * Implements Create's _initialize() method. */ - _initialize: function() { - // Sets the state to 'activating' upon clicking the element. - var that = this; - this.element.on("click.edit", function(event) { - event.stopPropagation(); - event.preventDefault(); - that.options.activating(); - }); - }, + _initialize: function() {}, /** * Makes this PropertyEditor widget react to state changes. diff --git a/core/modules/edit/js/views/menu-view.js b/core/modules/edit/js/views/menu-view.js index ac7c4e4..0f9ecc8 100644 --- a/core/modules/edit/js/views/menu-view.js +++ b/core/modules/edit/js/views/menu-view.js @@ -64,6 +64,8 @@ Drupal.edit.views.MenuView = Backbone.View.extend({ Drupal.toolbar.setHeight(); } } + // Let other modules respond to the edit mode change. + $(document).trigger('drupalEditMode', {'editable': !isViewing}); }, /** * Handles clicks on the edit tab of the toolbar. diff --git a/core/modules/edit/js/views/propertyeditordecoration-view.js b/core/modules/edit/js/views/propertyeditordecoration-view.js index 0eb4e45..aabd72c 100644 --- a/core/modules/edit/js/views/propertyeditordecoration-view.js +++ b/core/modules/edit/js/views/propertyeditordecoration-view.js @@ -17,8 +17,6 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({ _widthAttributeIsEmpty: null, events: { - 'mouseenter.edit' : 'onMouseEnter', - 'mouseleave.edit' : 'onMouseLeave', 'tabIn.edit': 'onMouseEnter', 'tabOut.edit': 'onMouseLeave' }, diff --git a/core/modules/edit/js/views/toolbar-view.js b/core/modules/edit/js/views/toolbar-view.js index 90f5db7..41e15ad 100644 --- a/core/modules/edit/js/views/toolbar-view.js +++ b/core/modules/edit/js/views/toolbar-view.js @@ -29,7 +29,10 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ 'click.edit button.label': 'onClickInfoLabel', 'mouseleave.edit': 'onMouseLeave', 'click.edit button.field-save': 'onClickSave', - 'click.edit button.field-close': 'onClickClose' + 'click.edit button.field-close': 'onClickClose', + 'mouseenter .pencil': 'onPencilMouseEnter', + 'mouseleave .pencil': 'onPencilMouseLeave', + 'click .pencil': 'onPencilClick' }, /** @@ -66,19 +69,26 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ stateChange: function(from, to) { switch (to) { case 'inactive': - // Nothing happens in this stage. + if (from) { + this.remove(); + } break; case 'candidate': - if (from !== 'inactive') { + if (from === 'inactive') { + this.render(); + } + else { + // Remove all toolgroups; they're no longer necessary. + this.$el + .removeClass('edit-highlighted edit-editing') + .find('.edit-toolbar .edit-toolgroup').remove(); if (from !== 'highlighted' && this.getEditUISetting('padding')) { this._unpad(); } - this.remove(); } break; case 'highlighted': // As soon as we highlight, make sure we have a toolbar in the DOM (with at least a title). - this.render(); this.startHighlight(); break; case 'activating': @@ -275,6 +285,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ } this.$el + .addClass('edit-highlighted') .find('.edit-toolbar') // Append the "info" toolgroup into the toolbar. .append(Drupal.theme('editToolgroup', { @@ -395,6 +406,10 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ this.$el.insertBefore(this.editor.element); } + // @todo: replace "Edit" with a proper (ARIA-like) string. + // @todo:
->