Fix bug with dropdown menu propagating the opening click event to a menu item (#3570)

* Prevent dropdown field menu from propagating a click event onto a menu item when the dropdown appears above the field
This commit is contained in:
Sam El-Husseini
2020-01-09 18:08:29 -08:00
committed by GitHub
parent e162f3b45f
commit 20e55b67d7
6 changed files with 49 additions and 7 deletions

View File

@@ -25,6 +25,7 @@ goog.provide('Blockly.Menu');
goog.require('Blockly.Component');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
@@ -37,6 +38,15 @@ goog.require('Blockly.utils.object');
Blockly.Menu = function() {
Blockly.Component.call(this);
/**
* Coordinates of the mousedown event that caused this menu to open. Used to
* prevent the consequent mouseup event due to a simple click from activating
* a menu item immediately.
* @type {?Blockly.utils.Coordinate}
* @package
*/
this.openingCoords = null;
/**
* This is the element that we will listen to the real focus events on.
* A value of -1 means no menuitem is highlighted.
@@ -432,6 +442,20 @@ Blockly.Menu.prototype.handleMouseOver_ = function(e) {
* @private
*/
Blockly.Menu.prototype.handleClick_ = function(e) {
var oldCoords = this.openingCoords;
// Clear out the saved opening coords immediately so they're not used twice.
this.openingCoords = null;
if (oldCoords && typeof e.clientX === 'number') {
var newCoords = new Blockly.utils.Coordinate(e.clientX, e.clientY);
if (Blockly.utils.Coordinate.distance(oldCoords, newCoords) < 1) {
// This menu was opened by a mousedown and we're handling the consequent
// click event. The coords haven't changed, meaning this was the same
// opening event. Don't do the usual behavior because the menu just popped
// up under the mouse and the user didn't mean to activate this item.
return;
}
}
var menuItem = this.getMenuItem(/** @type {Node} */ (e.target));
if (menuItem && menuItem.handleClick(e)) {

View File

@@ -204,6 +204,8 @@ Blockly.Field.prototype.getText_;
* An optional method that can be defined to show an editor when the field is
* clicked. Blockly will automatically set the field as clickable if this
* method is defined.
* @param {Event=} opt_e Optional mouse event that triggered the field to open,
* or undefined if triggered programatically.
* @return {void}
* @protected
*/
@@ -602,11 +604,13 @@ Blockly.Field.prototype.render_ = function() {
/**
* Show an editor when the field is clicked only if the field is clickable.
* @param {Event=} opt_e Optional mouse event that triggered the field to open,
* or undefined if triggered programatically.
* @package
*/
Blockly.Field.prototype.showEditor = function() {
Blockly.Field.prototype.showEditor = function(opt_e) {
if (this.isClickable()) {
this.showEditor_();
this.showEditor_(opt_e);
}
};

View File

@@ -256,15 +256,17 @@ Blockly.FieldAngle.prototype.render_ = function() {
/**
* Create and show the angle field's editor.
* @param {Event=} opt_e Optional mouse event that triggered the field to open,
* or undefined if triggered programatically.
* @private
*/
Blockly.FieldAngle.prototype.showEditor_ = function() {
Blockly.FieldAngle.prototype.showEditor_ = function(opt_e) {
// Mobile browsers have issues with in-line textareas (focus & keyboards).
var noFocus =
Blockly.utils.userAgent.MOBILE ||
Blockly.utils.userAgent.ANDROID ||
Blockly.utils.userAgent.IPAD;
Blockly.FieldAngle.superClass_.showEditor_.call(this, noFocus);
Blockly.FieldAngle.superClass_.showEditor_.call(this, opt_e, noFocus);
var editor = this.dropdownCreate_();
Blockly.DropDownDiv.getContentDiv().appendChild(editor);

View File

@@ -34,6 +34,7 @@ goog.require('Blockly.MenuItem');
goog.require('Blockly.navigation');
goog.require('Blockly.utils');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Size');
@@ -267,10 +268,18 @@ Blockly.FieldDropdown.prototype.createSVGArrow_ = function() {
/**
* Create a dropdown menu under the text.
* @param {Event=} opt_e Optional mouse event that triggered the field to open,
* or undefined if triggered programatically.
* @private
*/
Blockly.FieldDropdown.prototype.showEditor_ = function() {
Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
this.menu_ = this.dropdownCreate_();
if (opt_e && typeof opt_e.clientX === 'number') {
this.menu_.openingCoords =
new Blockly.utils.Coordinate(opt_e.clientX, opt_e.clientY);
} else {
this.menu_.openingCoords = null;
}
// Element gets created in render.
this.menu_.render(Blockly.DropDownDiv.getContentDiv());
Blockly.utils.dom.addClass(

View File

@@ -285,11 +285,14 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) {
/**
* Show the inline free-text editor on top of the text.
* @param {Event=} _opt_e Optional mouse event that triggered the field to open,
* or undefined if triggered programatically.
* @param {boolean=} opt_quietInput True if editor should be created without
* focus. Defaults to false.
* @protected
*/
Blockly.FieldTextInput.prototype.showEditor_ = function(opt_quietInput) {
Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e,
opt_quietInput) {
this.workspace_ = this.sourceBlock_.workspace;
var quietInput = opt_quietInput || false;
if (!quietInput && (Blockly.utils.userAgent.MOBILE ||

View File

@@ -731,7 +731,7 @@ Blockly.Gesture.prototype.doBubbleClick_ = function() {
* @private
*/
Blockly.Gesture.prototype.doFieldClick_ = function() {
this.startField_.showEditor();
this.startField_.showEditor(this.mostRecentEvent_);
this.bringBlockToFront_();
};