Type fixes for Dropdown / Widget divs (#3911)

* Stricter typechecks in dropdown and widget div
This commit is contained in:
Sam El-Husseini
2020-05-28 08:42:26 -07:00
committed by GitHub
parent 2b2ff909a9
commit acfc335870
6 changed files with 124 additions and 77 deletions

View File

@@ -54,10 +54,11 @@ goog.requireType('Blockly.ICopyable');
Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
// Create core elements for the block.
/**
* @type {!SVGElement}
* @type {!SVGGElement}
* @private
*/
this.svgGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null);
this.svgGroup_ = /** @type {!SVGGElement} */ (
Blockly.utils.dom.createSvgElement('g', {}, null));
this.svgGroup_.translate_ = '';
/**
@@ -931,7 +932,7 @@ Blockly.BlockSvg.prototype.setInsertionMarker = function(insertionMarker) {
/**
* Return the root node of the SVG or null if none exists.
* @return {!SVGElement} The root SVG node (probably a group).
* @return {!SVGGElement} The root SVG node (probably a group).
*/
Blockly.BlockSvg.prototype.getSvgRoot = function() {
return this.svgGroup_;

View File

@@ -24,6 +24,7 @@ goog.require('Blockly.Msg');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.userAgent');
goog.require('Blockly.Xml');
@@ -109,12 +110,12 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) {
var viewportBBox = Blockly.utils.getViewportBBox();
// This one is just a point, but we'll pretend that it's a rect so we can use
// some helper functions.
var anchorBBox = {
top: e.clientY + viewportBBox.top,
bottom: e.clientY + viewportBBox.top,
left: e.clientX + viewportBBox.left,
right: e.clientX + viewportBBox.left
};
var anchorBBox = new Blockly.utils.Rect(
e.clientY + viewportBBox.top,
e.clientY + viewportBBox.top,
e.clientX + viewportBBox.left,
e.clientX + viewportBBox.left
);
Blockly.ContextMenu.createWidget_(menu);
var menuSize = menu.getSize();

View File

@@ -19,6 +19,9 @@ goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.math');
goog.require('Blockly.utils.style');
goog.requireType('Blockly.utils.Rect');
goog.requireType('Blockly.utils.Size');
/**
* Class for drop-down div.
@@ -117,6 +120,35 @@ Blockly.DropDownDiv.rendererClassName_ = '';
*/
Blockly.DropDownDiv.themeClassName_ = '';
/**
* Dropdown bounds info object used to encapsulate sizing information about a
* bounding element (bounding box and width/height).
* @typedef {{
* top:number,
* left:number,
* bottom:number,
* right:number,
* width:number,
* height:number
* }}
*/
Blockly.DropDownDiv.BoundsInfo;
/**
* Dropdown position metrics.
* @typedef {{
* initialX:number,
* initialY:number,
* finalX:number,
* finalY:number,
* arrowX:?number,
* arrowY:?number,
* arrowAtTop:?boolean,
* arrowVisible:boolean
* }}
*/
Blockly.DropDownDiv.PositionMetrics;
/**
* Create and insert the DOM element for this div.
* @package
@@ -214,7 +246,7 @@ Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) {
* and the secondary position above the block. Drop-down will be
* constrained to the block's workspace.
* @param {!Blockly.Field} field The field showing the drop-down.
* @param {!Blockly.Block} block Block to position the drop-down around.
* @param {!Blockly.BlockSvg} block Block to position the drop-down around.
* @param {Function=} opt_onHide Optional callback for when the drop-down is
* hidden.
* @param {number=} opt_secondaryYOffset Optional Y offset for above-block
@@ -250,7 +282,7 @@ Blockly.DropDownDiv.showPositionedByField = function(field,
/**
* Get the scaled bounding box of a block.
* @param {!Blockly.Block} block The block.
* @param {!Blockly.BlockSvg} block The block.
* @return {!Blockly.utils.Rect} The scaled bounding box of the block.
* @private
*/
@@ -302,14 +334,15 @@ Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field,
if (opt_secondaryYOffset) {
secondaryY += opt_secondaryYOffset;
}
var sourceBlock = field.getSourceBlock();
var sourceBlock = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock());
// Set bounds to main workspace; show the drop-down.
var workspace = sourceBlock.workspace;
while (workspace.options.parentWorkspace) {
workspace = workspace.options.parentWorkspace;
workspace = /** @type {!Blockly.WorkspaceSvg} */ (
workspace.options.parentWorkspace);
}
Blockly.DropDownDiv.setBoundsElement(
workspace.getParentSvg().parentNode);
/** @type {Element} */ (workspace.getParentSvg().parentNode));
return Blockly.DropDownDiv.show(
field, sourceBlock.RTL,
primaryX, primaryY, secondaryX, secondaryY, opt_onHide);
@@ -344,10 +377,11 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
var div = Blockly.DropDownDiv.DIV_;
div.style.direction = rtl ? 'rtl' : 'ltr';
var mainWorkspace =
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
Blockly.DropDownDiv.rendererClassName_ =
Blockly.getMainWorkspace().getRenderer().getClassName();
Blockly.DropDownDiv.themeClassName_ =
Blockly.getMainWorkspace().getTheme().getClassName();
mainWorkspace.getRenderer().getClassName();
Blockly.DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.rendererClassName_);
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.themeClassName_);
@@ -366,8 +400,8 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
/**
* Get sizing info about the bounding element.
* @return {!Object} An object containing size information about the bounding
* element (bounding box and width/height).
* @return {!Blockly.DropDownDiv.BoundsInfo} An object containing size
* information about the bounding element (bounding box and width/height).
* @private
*/
Blockly.DropDownDiv.getBoundsInfo_ = function() {
@@ -392,11 +426,11 @@ Blockly.DropDownDiv.getBoundsInfo_ = function() {
* @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px.
* @param {number} secondaryX Secondary/alternative origin point x,
* in absolute px.
* in absolute px.
* @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px.
* @return {Object} Various final metrics, including rendered positions
* for drop-down and arrow.
* in absolute px.
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow.
* @private
*/
Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY,
@@ -435,12 +469,12 @@ Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY,
* Get the metrics for positioning the div below the source.
* @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px.
* @param {!Object} boundsInfo An object containing size information about the
* bounding element (bounding box and width/height).
* @param {!Object} divSize An object containing information about the size
* of the DropDownDiv (width & height).
* @return {Object} Various final metrics, including rendered positions
* for drop-down and arrow.
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
* information about the bounding element (bounding box and width/height).
* @param {!Blockly.utils.Size} divSize An object containing information about
* the size of the DropDownDiv (width & height).
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow.
* @private
*/
Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
@@ -468,15 +502,15 @@ Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
/**
* Get the metrics for positioning the div above the source.
* @param {number} secondaryX Secondary/alternative origin point x,
* in absolute px.
* in absolute px.
* @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px.
* @param {!Object} boundsInfo An object containing size information about the
* bounding element (bounding box and width/height).
* @param {!Object} divSize An object containing information about the size
* of the DropDownDiv (width & height).
* @return {Object} Various final metrics, including rendered positions
* for drop-down and arrow.
* in absolute px.
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
* information about the bounding element (bounding box and width/height).
* @param {!Blockly.utils.Size} divSize An object containing information about
* the size of the DropDownDiv (width & height).
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow.
* @private
*/
Blockly.DropDownDiv.getPositionAboveMetrics_ = function(
@@ -505,12 +539,12 @@ Blockly.DropDownDiv.getPositionAboveMetrics_ = function(
/**
* Get the metrics for positioning the div at the top of the page.
* @param {number} sourceX Desired origin point x, in absolute px.
* @param {!Object} boundsInfo An object containing size information about the
* bounding element (bounding box and width/height).
* @param {!Object} divSize An object containing information about the size
* of the DropDownDiv (width & height).
* @return {Object} Various final metrics, including rendered positions
* for drop-down and arrow.
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
* information about the bounding element (bounding box and width/height).
* @param {!Blockly.utils.Size} divSize An object containing information about
* the size of the DropDownDiv (width & height).
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow.
* @private
*/
Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
@@ -525,6 +559,9 @@ Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
initialY : 0,
finalX: xCoords.divX, // X position remains constant during animation.
finalY: 0, // Y position remains constant during animation.
arrowAtTop: null,
arrowX: null,
arrowY: null,
arrowVisible: false
};
};
@@ -653,7 +690,8 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() {
Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.themeClassName_);
Blockly.DropDownDiv.themeClassName_ = '';
}
Blockly.getMainWorkspace().markFocused();
(/** @type {!Blockly.WorkspaceSvg} */ (
Blockly.getMainWorkspace())).markFocused();
};
/**
@@ -704,7 +742,7 @@ Blockly.DropDownDiv.positionInternal_ = function(
var dy = finalY - initialY;
div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
return metrics.arrowAtTop;
return !!metrics.arrowAtTop;
};
/**
@@ -720,7 +758,7 @@ Blockly.DropDownDiv.repositionForWindowResize = function() {
// it.
if (Blockly.DropDownDiv.owner_) {
var field = /** @type {!Blockly.Field} */ (Blockly.DropDownDiv.owner_);
var block = Blockly.DropDownDiv.owner_.getSourceBlock();
var block = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock());
var bBox = Blockly.DropDownDiv.positionToField_ ?
Blockly.DropDownDiv.getScaledBboxOfField_(field) :
Blockly.DropDownDiv.getScaledBboxOfBlock_(block);

View File

@@ -19,6 +19,7 @@ goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Gesture');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.Size');
goog.require('Blockly.utils.style');
goog.require('Blockly.utils.userAgent');
@@ -716,8 +717,8 @@ Blockly.Field.prototype.getSize = function() {
/**
* Returns the bounding box of the rendered field, accounting for workspace
* scaling.
* @return {!Object} An object with top, bottom, left, and right in pixels
* relative to the top left corner of the page (window coordinates).
* @return {!Blockly.utils.Rect} An object with top, bottom, left, and right in
* pixels relative to the top left corner of the page (window coordinates).
* @package
*/
Blockly.Field.prototype.getScaledBBox = function() {
@@ -750,12 +751,12 @@ Blockly.Field.prototype.getScaledBBox = function() {
var scaledWidth = bBox.width;
var scaledHeight = bBox.height;
}
return {
top: xy.y,
bottom: xy.y + scaledHeight,
left: xy.x,
right: xy.x + scaledWidth
};
return new Blockly.utils.Rect(
xy.y,
xy.y + scaledHeight,
xy.x,
xy.x + scaledWidth
);
};
/**

View File

@@ -23,6 +23,7 @@ goog.require('Blockly.constants');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.global');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.string');
goog.require('Blockly.utils.style');
goog.require('Blockly.utils.userAgent');
@@ -496,19 +497,19 @@ Blockly.utils.runAfterPageLoad = function(fn) {
/**
* Get the position of the current viewport in window coordinates. This takes
* scroll into account.
* @return {!Object} An object containing window width, height, and scroll
* position in window coordinates.
* @return {!Blockly.utils.Rect} An object containing window width, height, and
* scroll position in window coordinates.
* @package
*/
Blockly.utils.getViewportBBox = function() {
// Pixels, in window coordinates.
var scrollOffset = Blockly.utils.style.getViewportPageOffset();
return {
right: document.documentElement.clientWidth + scrollOffset.x,
bottom: document.documentElement.clientHeight + scrollOffset.y,
top: scrollOffset.y,
left: scrollOffset.x
};
return new Blockly.utils.Rect(
scrollOffset.y,
document.documentElement.clientHeight + scrollOffset.y,
scrollOffset.x,
document.documentElement.clientWidth + scrollOffset.x
);
};
/**

View File

@@ -20,6 +20,9 @@ goog.provide('Blockly.WidgetDiv');
goog.require('Blockly.utils.style');
goog.requireType('Blockly.utils.Rect');
goog.requireType('Blockly.utils.Size');
/**
* The object currently using this container.
@@ -80,10 +83,11 @@ Blockly.WidgetDiv.show = function(newOwner, rtl, dispose) {
var div = Blockly.WidgetDiv.DIV;
div.style.direction = rtl ? 'rtl' : 'ltr';
div.style.display = 'block';
var mainWorkspace =
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
Blockly.WidgetDiv.rendererClassName_ =
Blockly.getMainWorkspace().getRenderer().getClassName();
Blockly.WidgetDiv.themeClassName_ =
Blockly.getMainWorkspace().getTheme().getClassName();
mainWorkspace.getRenderer().getClassName();
Blockly.WidgetDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.rendererClassName_);
Blockly.utils.dom.addClass(div, Blockly.WidgetDiv.themeClassName_);
};
@@ -113,7 +117,8 @@ Blockly.WidgetDiv.hide = function() {
Blockly.utils.dom.removeClass(div, Blockly.WidgetDiv.themeClassName_);
Blockly.WidgetDiv.themeClassName_ = '';
}
Blockly.getMainWorkspace().markFocused();
(/** @type {!Blockly.WorkspaceSvg} */ (
Blockly.getMainWorkspace())).markFocused();
};
/**
@@ -154,12 +159,12 @@ Blockly.WidgetDiv.positionInternal_ = function(x, y, height) {
* The widget should be placed adjacent to but not overlapping the anchor
* rectangle. The preferred position is directly below and aligned to the left
* (LTR) or right (RTL) side of the anchor.
* @param {!Object} viewportBBox The bounding rectangle of the current viewport,
* @param {!Blockly.utils.Rect} viewportBBox The bounding rectangle of the
* current viewport, in window coordinates.
* @param {!Blockly.utils.Rect} anchorBBox The bounding rectangle of the anchor,
* in window coordinates.
* @param {!Object} anchorBBox The bounding rectangle of the anchor, in window
* coordinates.
* @param {!Blockly.utils.Size} widgetSize The size of the widget that is inside the
* widget div, in window coordinates.
* @param {!Blockly.utils.Size} widgetSize The size of the widget that is inside
* the widget div, in window coordinates.
* @param {boolean} rtl Whether the workspace is in RTL mode. This determines
* horizontal alignment.
* @package
@@ -180,10 +185,10 @@ Blockly.WidgetDiv.positionWithAnchor = function(viewportBBox, anchorBBox,
/**
* Calculate an x position (in window coordinates) such that the widget will not
* be offscreen on the right or left.
* @param {!Object} viewportBBox The bounding rectangle of the current viewport,
* @param {!Blockly.utils.Rect} viewportBBox The bounding rectangle of the
* current viewport, in window coordinates.
* @param {!Blockly.utils.Rect} anchorBBox The bounding rectangle of the anchor,
* in window coordinates.
* @param {!Object} anchorBBox The bounding rectangle of the anchor, in window
* coordinates.
* @param {Blockly.utils.Size} widgetSize The dimensions of the widget inside the
* widget div.
* @param {boolean} rtl Whether the Blockly workspace is in RTL mode.
@@ -212,10 +217,10 @@ Blockly.WidgetDiv.calculateX_ = function(viewportBBox, anchorBBox, widgetSize,
/**
* Calculate a y position (in window coordinates) such that the widget will not
* be offscreen on the top or bottom.
* @param {!Object} viewportBBox The bounding rectangle of the current viewport,
* @param {!Blockly.utils.Rect} viewportBBox The bounding rectangle of the
* current viewport, in window coordinates.
* @param {!Blockly.utils.Rect} anchorBBox The bounding rectangle of the anchor,
* in window coordinates.
* @param {!Object} anchorBBox The bounding rectangle of the anchor, in window
* coordinates.
* @param {Blockly.utils.Size} widgetSize The dimensions of the widget inside the
* widget div.
* @return {number} A valid y-coordinate for the top left corner of the widget