Field dropdown with an SVG arrow (#3442)

* Add SVG arrow as an option on the dropdown field
This commit is contained in:
Sam El-Husseini
2019-11-14 15:00:49 -08:00
committed by GitHub
parent 564af764d0
commit c868a86bdd
4 changed files with 140 additions and 25 deletions

View File

@@ -114,11 +114,18 @@ Blockly.FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
this.imageElement_ = null;
/**
* SVG arrow element.
* Tspan based arrow element.
* @type {SVGTSpanElement}
* @private
*/
this.arrow_ = null;
/**
* SVG based arrow element.
* @type {SVGElement}
* @private
*/
this.svgArrow_ = null;
};
Blockly.utils.object.inherits(Blockly.FieldDropdown, Blockly.Field);
@@ -198,14 +205,22 @@ Blockly.FieldDropdown.prototype.initView = function() {
Blockly.FieldDropdown.superClass_.initView.call(this);
this.imageElement_ = /** @type {!SVGImageElement} */
(Blockly.utils.dom.createSvgElement('image',
{
'y': Blockly.FieldDropdown.IMAGE_Y_OFFSET
}, this.fieldGroup_));
(Blockly.utils.dom.createSvgElement('image', {}, this.fieldGroup_));
if (this.constants_.FIELD_DROPDOWN_SVG_ARROW) {
this.createSVGArrow_();
} else {
this.createTextArrow_();
}
};
/**
* Create a tspan based arrow.
* @protected
*/
Blockly.FieldDropdown.prototype.createTextArrow_ = function() {
this.arrow_ = /** @type {!SVGTSpanElement} */
(Blockly.utils.dom.createSvgElement('tspan',
{}, this.textElement_));
(Blockly.utils.dom.createSvgElement('tspan', {}, this.textElement_));
this.arrow_.appendChild(document.createTextNode(
this.sourceBlock_.RTL ?
Blockly.FieldDropdown.ARROW_CHAR + ' ' :
@@ -217,6 +232,19 @@ Blockly.FieldDropdown.prototype.initView = function() {
}
};
/**
* Create an SVG based arrow.
* @protected
*/
Blockly.FieldDropdown.prototype.createSVGArrow_ = function() {
this.svgArrow_ = Blockly.utils.dom.createSvgElement('image', {
'height': this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px',
'width': this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px'
}, this.fieldGroup_);
this.svgArrow_.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.constants_.FIELD_DROPDOWN_SVG_ARROW_DATAURI);
};
/**
* Create a dropdown menu under the text.
* @private
@@ -242,6 +270,8 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() {
/** @type {!Element} */ (this.selectedMenuItem_.getElement()),
/** @type {!Element} */ (this.menu_.getElement()));
}
this.applyColour();
};
/**
@@ -296,6 +326,7 @@ Blockly.FieldDropdown.prototype.dropdownDispose_ = function() {
}
this.menu_ = null;
this.selectedMenuItem_ = null;
this.applyColour();
};
/**
@@ -469,6 +500,17 @@ Blockly.FieldDropdown.prototype.doValueUpdate_ = function(newValue) {
* @package
*/
Blockly.FieldDropdown.prototype.applyColour = function() {
if (this.borderRect_) {
this.borderRect_.setAttribute('stroke',
this.sourceBlock_.style.colourTertiary);
if (this.menu_) {
this.borderRect_.setAttribute('fill',
this.sourceBlock_.style.colourTertiary);
} else {
this.borderRect_.setAttribute('fill',
this.sourceBlock_.style.colourPrimary);
}
}
// Update arrow's colour.
if (this.sourceBlock_ && this.arrow_) {
if (this.sourceBlock_.isShadow()) {
@@ -514,18 +556,26 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
this.imageElement_.setAttribute('height', imageJson.height);
this.imageElement_.setAttribute('width', imageJson.width);
var arrowWidth = Blockly.utils.dom.getFastTextWidth(
/** @type {!SVGTSpanElement} */ (this.arrow_),
this.constants_.FIELD_TEXT_FONTSIZE,
this.constants_.FIELD_TEXT_FONTWEIGHT,
this.constants_.FIELD_TEXT_FONTFAMILY);
var imageHeight = Number(imageJson.height);
var imageWidth = Number(imageJson.width);
// Height and width include the border rect.
this.size_.height = imageHeight + Blockly.FieldDropdown.IMAGE_Y_PADDING;
this.size_.height = Math.max(
this.constants_.FIELD_DROPDOWN_BORDER_RECT_HEIGHT,
imageHeight + Blockly.FieldDropdown.IMAGE_Y_PADDING);
var halfHeight = this.size_.height / 2;
var xPadding = this.constants_.FIELD_BORDER_RECT_X_PADDING;
var arrowWidth = 0;
if (this.svgArrow_) {
arrowWidth = this.positionSVGArrow_(imageWidth + xPadding, halfHeight -
this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2);
} else {
arrowWidth = Blockly.utils.dom.getFastTextWidth(
/** @type {!SVGTSpanElement} */ (this.arrow_),
this.constants_.FIELD_TEXT_FONTSIZE,
this.constants_.FIELD_TEXT_FONTWEIGHT,
this.constants_.FIELD_TEXT_FONTFAMILY);
}
this.size_.width = imageWidth + arrowWidth + xPadding * 2;
if (this.sourceBlock_.RTL) {
@@ -534,12 +584,12 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
this.imageElement_.setAttribute('x', imageX);
this.textElement_.setAttribute('x', arrowX);
} else {
var arrowX =
imageWidth + arrowWidth + xPadding + 1;
var arrowX = imageWidth + arrowWidth + xPadding + 1;
this.textElement_.setAttribute('text-anchor', 'end');
this.textElement_.setAttribute('x', arrowX);
this.imageElement_.setAttribute('x', xPadding);
}
this.imageElement_.setAttribute('y', halfHeight - imageHeight / 2);
};
/**
@@ -549,27 +599,55 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
Blockly.FieldDropdown.prototype.renderSelectedText_ = function() {
// Retrieves the selected option to display through getText_.
this.textContent_.nodeValue = this.getDisplayText_();
Blockly.utils.dom.addClass(/** @type {!Element} */ (this.textElement_),
'blocklyDropdownText');
this.textElement_.setAttribute('text-anchor', 'start');
this.textElement_.setAttribute('x',
this.constants_.FIELD_BORDER_RECT_X_PADDING);
// Height and width include the border rect.
this.size_.height = Math.max(
this.constants_.FIELD_DROPDOWN_BORDER_RECT_HEIGHT,
this.constants_.FIELD_TEXT_HEIGHT +
this.constants_.FIELD_BORDER_RECT_Y_PADDING * 2);
this.size_.width = Blockly.utils.dom.getFastTextWidth(this.textElement_,
this.constants_.FIELD_TEXT_HEIGHT);
var halfHeight = this.size_.height / 2;
var textWidth = Blockly.utils.dom.getFastTextWidth(this.textElement_,
this.constants_.FIELD_TEXT_FONTSIZE,
this.constants_.FIELD_TEXT_FONTWEIGHT,
this.constants_.FIELD_TEXT_FONTFAMILY) +
this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
this.constants_.FIELD_TEXT_FONTFAMILY);
var xPadding = this.constants_.FIELD_BORDER_RECT_X_PADDING;
var arrowWidth = 0;
if (this.svgArrow_) {
arrowWidth = this.positionSVGArrow_(textWidth + xPadding, halfHeight -
this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2);
}
this.size_.width = textWidth + arrowWidth + xPadding * 2;
this.textElement_.setAttribute('y', this.size_.height / 2);
this.textElement_.setAttribute('x', this.sourceBlock_.RTL ?
this.size_.width - textWidth - xPadding : xPadding);
this.textElement_.setAttribute('y', halfHeight);
if (!this.constants_.FIELD_TEXT_BASELINE_CENTER) {
this.textElement_.setAttribute('dy',
this.constants_.FIELD_TEXT_BASELINE_Y - this.size_.height / 2);
this.constants_.FIELD_TEXT_BASELINE_Y - halfHeight);
}
};
/**
* Position a drop-down arrow at the appropriate location at render-time.
* @param {number} x X position the arrow is being rendered at, in px.
* @param {number} y Y position the arrow is being rendered at, in px.
* @return {number} Amount of space the arrow is taking up, in px.
* @private
*/
Blockly.FieldDropdown.prototype.positionSVGArrow_ = function(x, y) {
if (!this.svgArrow_) {
return 0;
}
var padding = this.constants_.FIELD_BORDER_RECT_X_PADDING;
var svgArrowSize = this.constants_.FIELD_DROPDOWN_SVG_ARROW_SIZE;
var arrowX = this.sourceBlock_.RTL ? padding : x + padding;
this.svgArrow_.setAttribute('transform',
'translate(' + arrowX + ',' + y + ')');
return svgArrowSize + padding;
};
/**
* Use the `getText_` developer hook to override the field's text
* representation. Get the selected option text. If the selected option is an

View File

@@ -208,6 +208,12 @@ Blockly.blockRendering.ConstantProvider = function() {
*/
this.FIELD_DROPDOWN_BORDER_RECT_HEIGHT = this.FIELD_BORDER_RECT_HEIGHT;
/**
* Whether or not a dropdown field uses a text or SVG arrow.
* @type {boolean}
*/
this.FIELD_DROPDOWN_SVG_ARROW = false;
/**
* A colour field's default width.
* @type {number}

View File

@@ -164,6 +164,36 @@ Blockly.zelos.ConstantProvider = function() {
*/
this.FIELD_DROPDOWN_BORDER_RECT_HEIGHT = 8 * this.GRID_UNIT;
/**
* @override
*/
this.FIELD_DROPDOWN_SVG_ARROW = true;
/**
* A dropdown field's SVG arrow size.
* @type {number}
* @const
*/
this.FIELD_DROPDOWN_SVG_ARROW_SIZE = 12;
/**
* A dropdown field's SVG arrow datauri.
* @type {string}
* @const
*/
this.FIELD_DROPDOWN_SVG_ARROW_DATAURI =
'data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllci' +
'AxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMi43MSIgaG' +
'VpZ2h0PSI4Ljc5IiB2aWV3Qm94PSIwIDAgMTIuNzEgOC43OSI+PHRpdGxlPmRyb3Bkb3duLW' +
'Fycm93PC90aXRsZT48ZyBvcGFjaXR5PSIwLjEiPjxwYXRoIGQ9Ik0xMi43MSwyLjQ0QTIuND' +
'EsMi40MSwwLDAsMSwxMiw0LjE2TDguMDgsOC4wOGEyLjQ1LDIuNDUsMCwwLDEtMy40NSwwTD' +
'AuNzIsNC4xNkEyLjQyLDIuNDIsMCwwLDEsMCwyLjQ0LDIuNDgsMi40OCwwLDAsMSwuNzEuNz' +
'FDMSwwLjQ3LDEuNDMsMCw2LjM2LDBTMTEuNzUsMC40NiwxMiwuNzFBMi40NCwyLjQ0LDAsMC' +
'wxLDEyLjcxLDIuNDRaIiBmaWxsPSIjMjMxZjIwIi8+PC9nPjxwYXRoIGQ9Ik02LjM2LDcuNz' +
'lhMS40MywxLjQzLDAsMCwxLTEtLjQyTDEuNDIsMy40NWExLjQ0LDEuNDQsMCwwLDEsMC0yYz' +
'AuNTYtLjU2LDkuMzEtMC41Niw5Ljg3LDBhMS40NCwxLjQ0LDAsMCwxLDAsMkw3LjM3LDcuMz' +
'dBMS40MywxLjQzLDAsMCwxLDYuMzYsNy43OVoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=';
/**
* The ID of the highlight glow filter, or the empty string if no filter is
* set.

1
media/dropdown-arrow.svg Normal file
View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="12.71" height="8.79" viewBox="0 0 12.71 8.79"><title>dropdown-arrow</title><g opacity="0.1"><path d="M12.71,2.44A2.41,2.41,0,0,1,12,4.16L8.08,8.08a2.45,2.45,0,0,1-3.45,0L0.72,4.16A2.42,2.42,0,0,1,0,2.44,2.48,2.48,0,0,1,.71.71C1,0.47,1.43,0,6.36,0S11.75,0.46,12,.71A2.44,2.44,0,0,1,12.71,2.44Z" fill="#231f20"/></g><path d="M6.36,7.79a1.43,1.43,0,0,1-1-.42L1.42,3.45a1.44,1.44,0,0,1,0-2c0.56-.56,9.31-0.56,9.87,0a1.44,1.44,0,0,1,0,2L7.37,7.37A1.43,1.43,0,0,1,6.36,7.79Z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 569 B