Merge pull request #5207 from alschmiedt/migrate_dropdown

Migrate dropdown to goog.module syntax
This commit is contained in:
alschmiedt
2021-08-24 13:08:43 -07:00
committed by GitHub
3 changed files with 289 additions and 306 deletions

View File

@@ -13,18 +13,22 @@
'use strict'; 'use strict';
goog.provide('Blockly.DropDownDiv'); goog.module('Blockly.DropDownDiv');
goog.module.declareLegacyNamespace();
goog.require('Blockly.common'); /* eslint-disable-next-line no-unused-vars */
goog.require('Blockly.utils.dom'); const BlockSvg = goog.requireType('Blockly.BlockSvg');
goog.require('Blockly.utils.math'); const Rect = goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.Rect'); /* eslint-disable-next-line no-unused-vars */
goog.require('Blockly.utils.style'); const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */
goog.requireType('Blockly.BlockSvg'); const Size = goog.requireType('Blockly.utils.Size');
goog.requireType('Blockly.Field'); /* eslint-disable-next-line no-unused-vars */
goog.requireType('Blockly.utils.Size'); const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
goog.requireType('Blockly.WorkspaceSvg'); const common = goog.require('Blockly.common');
const dom = goog.require('Blockly.utils.dom');
const math = goog.require('Blockly.utils.math');
const style = goog.require('Blockly.utils.style');
/** /**
@@ -32,30 +36,7 @@ goog.requireType('Blockly.WorkspaceSvg');
* @constructor * @constructor
* @package * @package
*/ */
Blockly.DropDownDiv = function() { const DropDownDiv = function() {};
};
/**
* Drop-downs will appear within the bounds of this element if possible.
* Set in Blockly.DropDownDiv.setBoundsElement.
* @type {Element}
* @private
*/
Blockly.DropDownDiv.boundsElement_ = null;
/**
* The object currently using the drop-down.
* @type {Object}
* @private
*/
Blockly.DropDownDiv.owner_ = null;
/**
* Whether the dropdown was positioned to a field or the source block.
* @type {?boolean}
* @private
*/
Blockly.DropDownDiv.positionToField_ = null;
/** /**
* Arrow size in px. Should match the value in CSS * Arrow size in px. Should match the value in CSS
@@ -63,7 +44,7 @@ Blockly.DropDownDiv.positionToField_ = null;
* @type {number} * @type {number}
* @const * @const
*/ */
Blockly.DropDownDiv.ARROW_SIZE = 16; DropDownDiv.ARROW_SIZE = 16;
/** /**
* Drop-down border size in px. Should match the value in CSS (need to position * Drop-down border size in px. Should match the value in CSS (need to position
@@ -71,7 +52,7 @@ Blockly.DropDownDiv.ARROW_SIZE = 16;
* @type {number} * @type {number}
* @const * @const
*/ */
Blockly.DropDownDiv.BORDER_SIZE = 1; DropDownDiv.BORDER_SIZE = 1;
/** /**
* Amount the arrow must be kept away from the edges of the main drop-down div, * Amount the arrow must be kept away from the edges of the main drop-down div,
@@ -79,21 +60,21 @@ Blockly.DropDownDiv.BORDER_SIZE = 1;
* @type {number} * @type {number}
* @const * @const
*/ */
Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING = 12; DropDownDiv.ARROW_HORIZONTAL_PADDING = 12;
/** /**
* Amount drop-downs should be padded away from the source, in px. * Amount drop-downs should be padded away from the source, in px.
* @type {number} * @type {number}
* @const * @const
*/ */
Blockly.DropDownDiv.PADDING_Y = 16; DropDownDiv.PADDING_Y = 16;
/** /**
* Length of animations in seconds. * Length of animations in seconds.
* @type {number} * @type {number}
* @const * @const
*/ */
Blockly.DropDownDiv.ANIMATION_TIME = 0.25; DropDownDiv.ANIMATION_TIME = 0.25;
/** /**
* Timer for animation out, to be cleared if we need to immediately hide * Timer for animation out, to be cleared if we need to immediately hide
@@ -101,28 +82,71 @@ Blockly.DropDownDiv.ANIMATION_TIME = 0.25;
* @type {?number} * @type {?number}
* @private * @private
*/ */
Blockly.DropDownDiv.animateOutTimer_ = null; DropDownDiv.animateOutTimer_ = null;
/** /**
* Callback for when the drop-down is hidden. * Callback for when the drop-down is hidden.
* @type {?Function} * @type {?Function}
* @private * @private
*/ */
Blockly.DropDownDiv.onHide_ = null; DropDownDiv.onHide_ = null;
/** /**
* A class name representing the current owner's workspace renderer. * A class name representing the current owner's workspace renderer.
* @type {string} * @type {string}
* @private * @private
*/ */
Blockly.DropDownDiv.rendererClassName_ = ''; DropDownDiv.rendererClassName_ = '';
/** /**
* A class name representing the current owner's workspace theme. * A class name representing the current owner's workspace theme.
* @type {string} * @type {string}
* @private * @private
*/ */
Blockly.DropDownDiv.themeClassName_ = ''; DropDownDiv.themeClassName_ = '';
/**
* The content element.
* @type {!Element}
* @private
*/
DropDownDiv.DIV_;
/**
* The content element.
* @type {!Element}
* @private
*/
DropDownDiv.content_;
/**
* The arrow element.
* @type {!Element}
* @private
*/
DropDownDiv.arrow_;
/**
* Drop-downs will appear within the bounds of this element if possible.
* Set in DropDownDiv.setBoundsElement.
* @type {?Element}
* @private
*/
DropDownDiv.boundsElement_ = null;
/**
* The object currently using the drop-down.
* @type {?Object}
* @private
*/
DropDownDiv.owner_ = null;
/**
* Whether the dropdown was positioned to a field or the source block.
* @type {?boolean}
* @private
*/
DropDownDiv.positionToField_ = null;
/** /**
* Dropdown bounds info object used to encapsulate sizing information about a * Dropdown bounds info object used to encapsulate sizing information about a
@@ -136,7 +160,7 @@ Blockly.DropDownDiv.themeClassName_ = '';
* height:number * height:number
* }} * }}
*/ */
Blockly.DropDownDiv.BoundsInfo; DropDownDiv.BoundsInfo;
/** /**
* Dropdown position metrics. * Dropdown position metrics.
@@ -151,87 +175,73 @@ Blockly.DropDownDiv.BoundsInfo;
* arrowVisible:boolean * arrowVisible:boolean
* }} * }}
*/ */
Blockly.DropDownDiv.PositionMetrics; DropDownDiv.PositionMetrics;
/** /**
* Create and insert the DOM element for this div. * Create and insert the DOM element for this div.
* @package * @package
*/ */
Blockly.DropDownDiv.createDom = function() { DropDownDiv.createDom = function() {
if (Blockly.DropDownDiv.DIV_) { if (DropDownDiv.DIV_) {
return; // Already created. return; // Already created.
} }
var div = document.createElement('div'); const containerDiv = document.createElement('div');
div.className = 'blocklyDropDownDiv'; containerDiv.className = 'blocklyDropDownDiv';
var container = Blockly.common.getParentContainer() || document.body; const parentDiv = Blockly.parentContainer || document.body;
container.appendChild(div); parentDiv.appendChild(containerDiv);
/**
* The div element.
* @type {!Element}
* @private
*/
Blockly.DropDownDiv.DIV_ = div;
var content = document.createElement('div'); DropDownDiv.DIV_ = containerDiv;
const content = document.createElement('div');
content.className = 'blocklyDropDownContent'; content.className = 'blocklyDropDownContent';
div.appendChild(content); DropDownDiv.DIV_.appendChild(content);
/** DropDownDiv.content_ = content;
* The content element.
* @type {!Element}
* @private
*/
Blockly.DropDownDiv.content_ = content;
var arrow = document.createElement('div'); const arrow = document.createElement('div');
arrow.className = 'blocklyDropDownArrow'; arrow.className = 'blocklyDropDownArrow';
div.appendChild(arrow); DropDownDiv.DIV_.appendChild(arrow);
/** DropDownDiv.arrow_ = arrow;
* The arrow element.
* @type {!Element}
* @private
*/
Blockly.DropDownDiv.arrow_ = arrow;
Blockly.DropDownDiv.DIV_.style.opacity = 0; DropDownDiv.DIV_.style.opacity = 0;
// Transition animation for transform: translate() and opacity. // Transition animation for transform: translate() and opacity.
Blockly.DropDownDiv.DIV_.style.transition = 'transform ' + DropDownDiv.DIV_.style.transition = 'transform ' +
Blockly.DropDownDiv.ANIMATION_TIME + 's, ' + DropDownDiv.ANIMATION_TIME + 's, ' +
'opacity ' + Blockly.DropDownDiv.ANIMATION_TIME + 's'; 'opacity ' + DropDownDiv.ANIMATION_TIME + 's';
// Handle focusin/out events to add a visual indicator when // Handle focusin/out events to add a visual indicator when
// a child is focused or blurred. // a child is focused or blurred.
div.addEventListener('focusin', function() { DropDownDiv.DIV_.addEventListener('focusin', function() {
Blockly.utils.dom.addClass(div, 'blocklyFocused'); dom.addClass(DropDownDiv.DIV_, 'blocklyFocused');
}); });
div.addEventListener('focusout', function() { DropDownDiv.DIV_.addEventListener('focusout', function() {
Blockly.utils.dom.removeClass(div, 'blocklyFocused'); dom.removeClass(DropDownDiv.DIV_, 'blocklyFocused');
}); });
}; };
/** /**
* Set an element to maintain bounds within. Drop-downs will appear * Set an element to maintain bounds within. Drop-downs will appear
* within the box of this element if possible. * within the box of this element if possible.
* @param {Element} boundsElement Element to bind drop-down to. * @param {?Element} boundsElement Element to bind drop-down to.
*/ */
Blockly.DropDownDiv.setBoundsElement = function(boundsElement) { DropDownDiv.setBoundsElement = function(boundsElement) {
Blockly.DropDownDiv.boundsElement_ = boundsElement; DropDownDiv.boundsElement_ = boundsElement;
}; };
/** /**
* Provide the div for inserting content into the drop-down. * Provide the div for inserting content into the drop-down.
* @return {!Element} Div to populate with content. * @return {!Element} Div to populate with content.
*/ */
Blockly.DropDownDiv.getContentDiv = function() { DropDownDiv.getContentDiv = function() {
return Blockly.DropDownDiv.content_; return DropDownDiv.content_;
}; };
/** /**
* Clear the content of the drop-down. * Clear the content of the drop-down.
*/ */
Blockly.DropDownDiv.clearContent = function() { DropDownDiv.clearContent = function() {
Blockly.DropDownDiv.content_.textContent = ''; DropDownDiv.content_.textContent = '';
Blockly.DropDownDiv.content_.style.width = ''; DropDownDiv.content_.style.width = '';
}; };
/** /**
@@ -239,9 +249,9 @@ Blockly.DropDownDiv.clearContent = function() {
* @param {string} backgroundColour Any CSS colour for the background. * @param {string} backgroundColour Any CSS colour for the background.
* @param {string} borderColour Any CSS colour for the border. * @param {string} borderColour Any CSS colour for the border.
*/ */
Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) { DropDownDiv.setColour = function(backgroundColour, borderColour) {
Blockly.DropDownDiv.DIV_.style.backgroundColor = backgroundColour; DropDownDiv.DIV_.style.backgroundColor = backgroundColour;
Blockly.DropDownDiv.DIV_.style.borderColor = borderColour; DropDownDiv.DIV_.style.borderColor = borderColour;
}; };
/** /**
@@ -249,19 +259,18 @@ Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) {
* by a particular block. The primary position will be below the block, * by a particular block. The primary position will be below the block,
* and the secondary position above the block. Drop-down will be * and the secondary position above the block. Drop-down will be
* constrained to the block's workspace. * constrained to the block's workspace.
* @param {!Blockly.Field} field The field showing the drop-down. * @param {!Field} field The field showing the drop-down.
* @param {!Blockly.BlockSvg} block Block to position the drop-down around. * @param {!BlockSvg} block Block to position the drop-down around.
* @param {Function=} opt_onHide Optional callback for when the drop-down is * @param {Function=} opt_onHide Optional callback for when the drop-down is
* hidden. * hidden.
* @param {number=} opt_secondaryYOffset Optional Y offset for above-block * @param {number=} opt_secondaryYOffset Optional Y offset for above-block
* positioning. * positioning.
* @return {boolean} True if the menu rendered below block; false if above. * @return {boolean} True if the menu rendered below block; false if above.
*/ */
Blockly.DropDownDiv.showPositionedByBlock = function(field, block, DropDownDiv.showPositionedByBlock = function(
opt_onHide, opt_secondaryYOffset) { field, block, opt_onHide, opt_secondaryYOffset) {
return Blockly.DropDownDiv.showPositionedByRect_( return showPositionedByRect(
Blockly.DropDownDiv.getScaledBboxOfBlock_(block), getScaledBboxOfBlock(block), field, opt_onHide, opt_secondaryYOffset);
field, opt_onHide, opt_secondaryYOffset);
}; };
/** /**
@@ -269,48 +278,45 @@ Blockly.DropDownDiv.showPositionedByBlock = function(field, block,
* by a particular field. The primary position will be below the field, * by a particular field. The primary position will be below the field,
* and the secondary position above the field. Drop-down will be * and the secondary position above the field. Drop-down will be
* constrained to the block's workspace. * constrained to the block's workspace.
* @param {!Blockly.Field} field The field to position the dropdown against. * @param {!Field} field The field to position the dropdown against.
* @param {Function=} opt_onHide Optional callback for when the drop-down is * @param {Function=} opt_onHide Optional callback for when the drop-down is
* hidden. * hidden.
* @param {number=} opt_secondaryYOffset Optional Y offset for above-block * @param {number=} opt_secondaryYOffset Optional Y offset for above-block
* positioning. * positioning.
* @return {boolean} True if the menu rendered below block; false if above. * @return {boolean} True if the menu rendered below block; false if above.
*/ */
Blockly.DropDownDiv.showPositionedByField = function(field, DropDownDiv.showPositionedByField = function(
opt_onHide, opt_secondaryYOffset) { field, opt_onHide, opt_secondaryYOffset) {
Blockly.DropDownDiv.positionToField_ = true; DropDownDiv.positionToField_ = true;
return Blockly.DropDownDiv.showPositionedByRect_( return showPositionedByRect(
Blockly.DropDownDiv.getScaledBboxOfField_(field), getScaledBboxOfField(field), field, opt_onHide, opt_secondaryYOffset);
field, opt_onHide, opt_secondaryYOffset);
}; };
const internal = {};
/** /**
* Get the scaled bounding box of a block. * Get the scaled bounding box of a block.
* @param {!Blockly.BlockSvg} block The block. * @param {!BlockSvg} block The block.
* @return {!Blockly.utils.Rect} The scaled bounding box of the block. * @return {!Rect} The scaled bounding box of the block.
* @private
*/ */
Blockly.DropDownDiv.getScaledBboxOfBlock_ = function(block) { const getScaledBboxOfBlock = function(block) {
var blockSvg = block.getSvgRoot(); const blockSvg = block.getSvgRoot();
var bBox = blockSvg.getBBox(); const bBox = blockSvg.getBBox();
var scale = block.workspace.scale; const scale = block.workspace.scale;
var scaledHeight = bBox.height * scale; const scaledHeight = bBox.height * scale;
var scaledWidth = bBox.width * scale; const scaledWidth = bBox.width * scale;
var xy = Blockly.utils.style.getPageOffset(blockSvg); const xy = style.getPageOffset(blockSvg);
return new Blockly.utils.Rect( return new Rect(xy.y, xy.y + scaledHeight, xy.x, xy.x + scaledWidth);
xy.y, xy.y + scaledHeight, xy.x, xy.x + scaledWidth);
}; };
/** /**
* Get the scaled bounding box of a field. * Get the scaled bounding box of a field.
* @param {!Blockly.Field} field The field. * @param {!Field} field The field.
* @return {!Blockly.utils.Rect} The scaled bounding box of the field. * @return {!Rect} The scaled bounding box of the field.
* @private
*/ */
Blockly.DropDownDiv.getScaledBboxOfField_ = function(field) { const getScaledBboxOfField = function(field) {
var bBox = field.getScaledBBox(); const bBox = field.getScaledBBox();
return new Blockly.utils.Rect( return new Rect(bBox.top, bBox.bottom, bBox.left, bBox.right);
bBox.top, bBox.bottom, bBox.left, bBox.right);
}; };
/** /**
@@ -318,38 +324,37 @@ Blockly.DropDownDiv.getScaledBboxOfField_ = function(field) {
* by a scaled bounding box. The primary position will be below the rect, * by a scaled bounding box. The primary position will be below the rect,
* and the secondary position above the rect. Drop-down will be constrained to * and the secondary position above the rect. Drop-down will be constrained to
* the block's workspace. * the block's workspace.
* @param {!Blockly.utils.Rect} bBox The scaled bounding box. * @param {!Rect} bBox The scaled bounding box.
* @param {!Blockly.Field} field The field to position the dropdown against. * @param {!Field} field The field to position the dropdown against.
* @param {Function=} opt_onHide Optional callback for when the drop-down is * @param {Function=} opt_onHide Optional callback for when the drop-down is
* hidden. * hidden.
* @param {number=} opt_secondaryYOffset Optional Y offset for above-block * @param {number=} opt_secondaryYOffset Optional Y offset for above-block
* positioning. * positioning.
* @return {boolean} True if the menu rendered below block; false if above. * @return {boolean} True if the menu rendered below block; false if above.
* @private
*/ */
Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field, const showPositionedByRect = function(
opt_onHide, opt_secondaryYOffset) { bBox, field, opt_onHide, opt_secondaryYOffset) {
// If we can fit it, render below the block. // If we can fit it, render below the block.
var primaryX = bBox.left + (bBox.right - bBox.left) / 2; const primaryX = bBox.left + (bBox.right - bBox.left) / 2;
var primaryY = bBox.bottom; const primaryY = bBox.bottom;
// If we can't fit it, render above the entire parent block. // If we can't fit it, render above the entire parent block.
var secondaryX = primaryX; const secondaryX = primaryX;
var secondaryY = bBox.top; let secondaryY = bBox.top;
if (opt_secondaryYOffset) { if (opt_secondaryYOffset) {
secondaryY += opt_secondaryYOffset; secondaryY += opt_secondaryYOffset;
} }
var sourceBlock = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock()); const sourceBlock = /** @type {!BlockSvg} */ (field.getSourceBlock());
// Set bounds to main workspace; show the drop-down. // Set bounds to main workspace; show the drop-down.
var workspace = sourceBlock.workspace; let workspace = sourceBlock.workspace;
while (workspace.options.parentWorkspace) { while (workspace.options.parentWorkspace) {
workspace = /** @type {!Blockly.WorkspaceSvg} */ ( workspace =
workspace.options.parentWorkspace); /** @type {!WorkspaceSvg} */ (workspace.options.parentWorkspace);
} }
Blockly.DropDownDiv.setBoundsElement( DropDownDiv.setBoundsElement(
/** @type {Element} */ (workspace.getParentSvg().parentNode)); /** @type {?Element} */ (workspace.getParentSvg().parentNode));
return Blockly.DropDownDiv.show( return DropDownDiv.show(
field, sourceBlock.RTL, field, sourceBlock.RTL, primaryX, primaryY, secondaryX, secondaryY,
primaryX, primaryY, secondaryX, secondaryY, opt_onHide); opt_onHide);
}; };
/** /**
@@ -360,7 +365,7 @@ Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field,
* will point there, and the container will be positioned below it. * will point there, and the container will be positioned below it.
* If we can't maintain the container bounds at the primary point, fall-back to * If we can't maintain the container bounds at the primary point, fall-back to
* the secondary point and position above. * the secondary point and position above.
* @param {Object} owner The object showing the drop-down * @param {?Object} owner The object showing the drop-down
* @param {boolean} rtl Right-to-left (true) or left-to-right (false). * @param {boolean} rtl Right-to-left (true) or left-to-right (false).
* @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px.
@@ -373,21 +378,20 @@ Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field,
* @return {boolean} True if the menu rendered at the primary origin point. * @return {boolean} True if the menu rendered at the primary origin point.
* @package * @package
*/ */
Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY, DropDownDiv.show = function(
secondaryX, secondaryY, opt_onHide) { owner, rtl, primaryX, primaryY, secondaryX, secondaryY, opt_onHide) {
Blockly.DropDownDiv.owner_ = owner; DropDownDiv.owner_ = owner;
Blockly.DropDownDiv.onHide_ = opt_onHide || null; DropDownDiv.onHide_ = opt_onHide || null;
// Set direction. // Set direction.
var div = Blockly.DropDownDiv.DIV_; const div = DropDownDiv.DIV_;
div.style.direction = rtl ? 'rtl' : 'ltr'; div.style.direction = rtl ? 'rtl' : 'ltr';
var mainWorkspace = const mainWorkspace =
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.common.getMainWorkspace()); /** @type {!WorkspaceSvg} */ (Blockly.getMainWorkspace());
Blockly.DropDownDiv.rendererClassName_ = DropDownDiv.rendererClassName_ = mainWorkspace.getRenderer().getClassName();
mainWorkspace.getRenderer().getClassName(); DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
Blockly.DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName(); dom.addClass(div, DropDownDiv.rendererClassName_);
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.rendererClassName_); dom.addClass(div, DropDownDiv.themeClassName_);
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.themeClassName_);
// When we change `translate` multiple times in close succession, // When we change `translate` multiple times in close succession,
// Chrome may choose to wait and apply them all at once. // Chrome may choose to wait and apply them all at once.
@@ -398,21 +402,19 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
// Using both `left`, `top` for the initial translation and then `translate` // Using both `left`, `top` for the initial translation and then `translate`
// for the animated transition to final X, Y is a workaround. // for the animated transition to final X, Y is a workaround.
return Blockly.DropDownDiv.positionInternal_( return positionInternal(primaryX, primaryY, secondaryX, secondaryY);
primaryX, primaryY, secondaryX, secondaryY);
}; };
/** /**
* Get sizing info about the bounding element. * Get sizing info about the bounding element.
* @return {!Blockly.DropDownDiv.BoundsInfo} An object containing size * @return {!DropDownDiv.BoundsInfo} An object containing size
* information about the bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @private
*/ */
Blockly.DropDownDiv.getBoundsInfo_ = function() { internal.getBoundsInfo = function() {
var boundPosition = Blockly.utils.style.getPageOffset( const boundPosition = style.getPageOffset(
/** @type {!Element} */ (Blockly.DropDownDiv.boundsElement_)); /** @type {!Element} */ (DropDownDiv.boundsElement_));
var boundSize = Blockly.utils.style.getSize( const boundSize = style.getSize(
/** @type {!Element} */ (Blockly.DropDownDiv.boundsElement_)); /** @type {!Element} */ (DropDownDiv.boundsElement_));
return { return {
left: boundPosition.x, left: boundPosition.x,
@@ -426,74 +428,65 @@ Blockly.DropDownDiv.getBoundsInfo_ = function() {
/** /**
* Helper to position the drop-down and the arrow, maintaining bounds. * Helper to position the drop-down and the arrow, maintaining bounds.
* See explanation of origin points in Blockly.DropDownDiv.show. * See explanation of origin points in DropDownDiv.show.
* @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px.
* @param {number} secondaryX Secondary/alternative origin point x, * @param {number} secondaryX Secondary/alternative origin point x,
* in absolute px. * in absolute px.
* @param {number} secondaryY Secondary/alternative origin point y, * @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px. * in absolute px.
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, * @return {!DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private
*/ */
Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY, internal.getPositionMetrics = function(
secondaryX, secondaryY) { primaryX, primaryY, secondaryX, secondaryY) {
var boundsInfo = Blockly.DropDownDiv.getBoundsInfo_(); const boundsInfo = internal.getBoundsInfo();
var divSize = Blockly.utils.style.getSize( const divSize = style.getSize(
/** @type {!Element} */ (Blockly.DropDownDiv.DIV_)); /** @type {!Element} */ (DropDownDiv.DIV_));
// Can we fit in-bounds below the target? // Can we fit in-bounds below the target?
if (primaryY + divSize.height < boundsInfo.bottom) { if (primaryY + divSize.height < boundsInfo.bottom) {
return Blockly.DropDownDiv.getPositionBelowMetrics_( return getPositionBelowMetrics(primaryX, primaryY, boundsInfo, divSize);
primaryX, primaryY, boundsInfo, divSize);
} }
// Can we fit in-bounds above the target? // Can we fit in-bounds above the target?
if (secondaryY - divSize.height > boundsInfo.top) { if (secondaryY - divSize.height > boundsInfo.top) {
return Blockly.DropDownDiv.getPositionAboveMetrics_( return getPositionAboveMetrics(secondaryX, secondaryY, boundsInfo, divSize);
secondaryX, secondaryY, boundsInfo, divSize);
} }
// Can we fit outside the workspace bounds (but inside the window) below? // Can we fit outside the workspace bounds (but inside the window) below?
if (primaryY + divSize.height < document.documentElement.clientHeight) { if (primaryY + divSize.height < document.documentElement.clientHeight) {
return Blockly.DropDownDiv.getPositionBelowMetrics_( return getPositionBelowMetrics(primaryX, primaryY, boundsInfo, divSize);
primaryX, primaryY, boundsInfo, divSize);
} }
// Can we fit outside the workspace bounds (but inside the window) above? // Can we fit outside the workspace bounds (but inside the window) above?
if (secondaryY - divSize.height > document.documentElement.clientTop) { if (secondaryY - divSize.height > document.documentElement.clientTop) {
return Blockly.DropDownDiv.getPositionAboveMetrics_( return getPositionAboveMetrics(secondaryX, secondaryY, boundsInfo, divSize);
secondaryX, secondaryY, boundsInfo, divSize);
} }
// Last resort, render at top of page. // Last resort, render at top of page.
return Blockly.DropDownDiv.getPositionTopOfPageMetrics_( return getPositionTopOfPageMetrics(primaryX, boundsInfo, divSize);
primaryX, boundsInfo, divSize);
}; };
/** /**
* Get the metrics for positioning the div below the source. * Get the metrics for positioning the div below the source.
* @param {number} primaryX Desired origin point x, in absolute px. * @param {number} primaryX Desired origin point x, in absolute px.
* @param {number} primaryY Desired origin point y, in absolute px. * @param {number} primaryY Desired origin point y, in absolute px.
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size * @param {!DropDownDiv.BoundsInfo} boundsInfo An object containing size
* information about the bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @param {!Blockly.utils.Size} divSize An object containing information about * @param {!Size} divSize An object containing information about
* the size of the DropDownDiv (width & height). * the size of the DropDownDiv (width & height).
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, * @return {!DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private
*/ */
Blockly.DropDownDiv.getPositionBelowMetrics_ = function( const getPositionBelowMetrics = function(
primaryX, primaryY, boundsInfo, divSize) { primaryX, primaryY, boundsInfo, divSize) {
const xCoords = DropDownDiv.getPositionX(
var xCoords = Blockly.DropDownDiv.getPositionX(
primaryX, boundsInfo.left, boundsInfo.right, divSize.width); primaryX, boundsInfo.left, boundsInfo.right, divSize.width);
var arrowY = -(Blockly.DropDownDiv.ARROW_SIZE / 2 + const arrowY = -(DropDownDiv.ARROW_SIZE / 2 + DropDownDiv.BORDER_SIZE);
Blockly.DropDownDiv.BORDER_SIZE); const finalY = primaryY + DropDownDiv.PADDING_Y;
var finalY = primaryY + Blockly.DropDownDiv.PADDING_Y;
return { return {
initialX: xCoords.divX, initialX: xCoords.divX,
initialY : primaryY, initialY: primaryY,
finalX: xCoords.divX, // X position remains constant during animation. finalX: xCoords.divX, // X position remains constant during animation.
finalY: finalY, finalY: finalY,
arrowX: xCoords.arrowX, arrowX: xCoords.arrowX,
@@ -509,28 +502,26 @@ Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
* in absolute px. * in absolute px.
* @param {number} secondaryY Secondary/alternative origin point y, * @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px. * in absolute px.
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size * @param {!DropDownDiv.BoundsInfo} boundsInfo An object containing size
* information about the bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @param {!Blockly.utils.Size} divSize An object containing information about * @param {!Size} divSize An object containing information about
* the size of the DropDownDiv (width & height). * the size of the DropDownDiv (width & height).
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, * @return {!DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private
*/ */
Blockly.DropDownDiv.getPositionAboveMetrics_ = function( const getPositionAboveMetrics = function(
secondaryX, secondaryY, boundsInfo, divSize) { secondaryX, secondaryY, boundsInfo, divSize) {
const xCoords = DropDownDiv.getPositionX(
var xCoords = Blockly.DropDownDiv.getPositionX(
secondaryX, boundsInfo.left, boundsInfo.right, divSize.width); secondaryX, boundsInfo.left, boundsInfo.right, divSize.width);
var arrowY = divSize.height - (Blockly.DropDownDiv.BORDER_SIZE * 2) - const arrowY = divSize.height - (DropDownDiv.BORDER_SIZE * 2) -
(Blockly.DropDownDiv.ARROW_SIZE / 2); (DropDownDiv.ARROW_SIZE / 2);
var finalY = secondaryY - divSize.height - Blockly.DropDownDiv.PADDING_Y; const finalY = secondaryY - divSize.height - DropDownDiv.PADDING_Y;
var initialY = secondaryY - divSize.height; // No padding on Y. const initialY = secondaryY - divSize.height; // No padding on Y.
return { return {
initialX: xCoords.divX, initialX: xCoords.divX,
initialY : initialY, initialY: initialY,
finalX: xCoords.divX, // X position remains constant during animation. finalX: xCoords.divX, // X position remains constant during animation.
finalY: finalY, finalY: finalY,
arrowX: xCoords.arrowX, arrowX: xCoords.arrowX,
@@ -543,24 +534,21 @@ Blockly.DropDownDiv.getPositionAboveMetrics_ = function(
/** /**
* Get the metrics for positioning the div at the top of the page. * Get the metrics for positioning the div at the top of the page.
* @param {number} sourceX Desired origin point x, in absolute px. * @param {number} sourceX Desired origin point x, in absolute px.
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size * @param {!DropDownDiv.BoundsInfo} boundsInfo An object containing size
* information about the bounding element (bounding box and width/height). * information about the bounding element (bounding box and width/height).
* @param {!Blockly.utils.Size} divSize An object containing information about * @param {!Size} divSize An object containing information about
* the size of the DropDownDiv (width & height). * the size of the DropDownDiv (width & height).
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics, * @return {!DropDownDiv.PositionMetrics} Various final metrics,
* including rendered positions for drop-down and arrow. * including rendered positions for drop-down and arrow.
* @private
*/ */
Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function( const getPositionTopOfPageMetrics = function(sourceX, boundsInfo, divSize) {
sourceX, boundsInfo, divSize) { const xCoords = DropDownDiv.getPositionX(
var xCoords = Blockly.DropDownDiv.getPositionX(
sourceX, boundsInfo.left, boundsInfo.right, divSize.width); sourceX, boundsInfo.left, boundsInfo.right, divSize.width);
// No need to provide arrow-specific information because it won't be visible. // No need to provide arrow-specific information because it won't be visible.
return { return {
initialX: xCoords.divX, initialX: xCoords.divX,
initialY : 0, initialY: 0,
finalX: xCoords.divX, // X position remains constant during animation. finalX: xCoords.divX, // X position remains constant during animation.
finalY: 0, // Y position remains constant during animation. finalY: 0, // Y position remains constant during animation.
arrowAtTop: null, arrowAtTop: null,
@@ -583,54 +571,50 @@ Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
* the x positions of the left side of the DropDownDiv and the arrow. * the x positions of the left side of the DropDownDiv and the arrow.
* @package * @package
*/ */
Blockly.DropDownDiv.getPositionX = function( DropDownDiv.getPositionX = function(
sourceX, boundsLeft, boundsRight, divWidth) { sourceX, boundsLeft, boundsRight, divWidth) {
var arrowX, divX; let arrowX, divX;
arrowX = divX = sourceX; arrowX = divX = sourceX;
// Offset the topLeft coord so that the dropdowndiv is centered. // Offset the topLeft coord so that the dropdowndiv is centered.
divX -= divWidth / 2; divX -= divWidth / 2;
// Fit the dropdowndiv within the bounds of the workspace. // Fit the dropdowndiv within the bounds of the workspace.
divX = Blockly.utils.math.clamp(boundsLeft, divX, boundsRight - divWidth); divX = math.clamp(boundsLeft, divX, boundsRight - divWidth);
// Offset the arrow coord so that the arrow is centered. // Offset the arrow coord so that the arrow is centered.
arrowX -= Blockly.DropDownDiv.ARROW_SIZE / 2; arrowX -= DropDownDiv.ARROW_SIZE / 2;
// Convert the arrow position to be relative to the top left of the div. // Convert the arrow position to be relative to the top left of the div.
var relativeArrowX = arrowX - divX; let relativeArrowX = arrowX - divX;
var horizPadding = Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING; const horizPadding = DropDownDiv.ARROW_HORIZONTAL_PADDING;
// Clamp the arrow position so that it stays attached to the dropdowndiv. // Clamp the arrow position so that it stays attached to the dropdowndiv.
relativeArrowX = Blockly.utils.math.clamp( relativeArrowX = math.clamp(
horizPadding, horizPadding, relativeArrowX,
relativeArrowX, divWidth - horizPadding - DropDownDiv.ARROW_SIZE);
divWidth - horizPadding - Blockly.DropDownDiv.ARROW_SIZE);
return { return {arrowX: relativeArrowX, divX: divX};
arrowX: relativeArrowX,
divX: divX
};
}; };
/** /**
* Is the container visible? * Is the container visible?
* @return {boolean} True if visible. * @return {boolean} True if visible.
*/ */
Blockly.DropDownDiv.isVisible = function() { DropDownDiv.isVisible = function() {
return !!Blockly.DropDownDiv.owner_; return !!DropDownDiv.owner_;
}; };
/** /**
* Hide the menu only if it is owned by the provided object. * Hide the menu only if it is owned by the provided object.
* @param {Object} owner Object which must be owning the drop-down to hide. * @param {?Object} owner Object which must be owning the drop-down to hide.
* @param {boolean=} opt_withoutAnimation True if we should hide the dropdown * @param {boolean=} opt_withoutAnimation True if we should hide the dropdown
* without animating. * without animating.
* @return {boolean} True if hidden. * @return {boolean} True if hidden.
*/ */
Blockly.DropDownDiv.hideIfOwner = function(owner, opt_withoutAnimation) { DropDownDiv.hideIfOwner = function(owner, opt_withoutAnimation) {
if (Blockly.DropDownDiv.owner_ === owner) { if (DropDownDiv.owner_ === owner) {
if (opt_withoutAnimation) { if (opt_withoutAnimation) {
Blockly.DropDownDiv.hideWithoutAnimation(); DropDownDiv.hideWithoutAnimation();
} else { } else {
Blockly.DropDownDiv.hide(); DropDownDiv.hide();
} }
return true; return true;
} }
@@ -640,37 +624,35 @@ Blockly.DropDownDiv.hideIfOwner = function(owner, opt_withoutAnimation) {
/** /**
* Hide the menu, triggering animation. * Hide the menu, triggering animation.
*/ */
Blockly.DropDownDiv.hide = function() { DropDownDiv.hide = function() {
// Start the animation by setting the translation and fading out. // Start the animation by setting the translation and fading out.
var div = Blockly.DropDownDiv.DIV_;
// Reset to (initialX, initialY) - i.e., no translation. // Reset to (initialX, initialY) - i.e., no translation.
div.style.transform = 'translate(0, 0)'; DropDownDiv.DIV_.style.transform = 'translate(0, 0)';
div.style.opacity = 0; DropDownDiv.DIV_.style.opacity = 0;
// Finish animation - reset all values to default. // Finish animation - reset all values to default.
Blockly.DropDownDiv.animateOutTimer_ = DropDownDiv.animateOutTimer_ = setTimeout(function() {
setTimeout(function() { DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.hideWithoutAnimation(); }, DropDownDiv.ANIMATION_TIME * 1000);
}, Blockly.DropDownDiv.ANIMATION_TIME * 1000); if (DropDownDiv.onHide_) {
if (Blockly.DropDownDiv.onHide_) { DropDownDiv.onHide_();
Blockly.DropDownDiv.onHide_(); DropDownDiv.onHide_ = null;
Blockly.DropDownDiv.onHide_ = null;
} }
}; };
/** /**
* Hide the menu, without animation. * Hide the menu, without animation.
*/ */
Blockly.DropDownDiv.hideWithoutAnimation = function() { DropDownDiv.hideWithoutAnimation = function() {
if (!Blockly.DropDownDiv.isVisible()) { if (!DropDownDiv.isVisible()) {
return; return;
} }
if (Blockly.DropDownDiv.animateOutTimer_) { if (DropDownDiv.animateOutTimer_) {
clearTimeout(Blockly.DropDownDiv.animateOutTimer_); clearTimeout(DropDownDiv.animateOutTimer_);
} }
// Reset style properties in case this gets called directly // Reset style properties in case this gets called directly
// instead of hide() - see discussion on #2551. // instead of hide() - see discussion on #2551.
var div = Blockly.DropDownDiv.DIV_; const div = DropDownDiv.DIV_;
div.style.transform = ''; div.style.transform = '';
div.style.left = ''; div.style.left = '';
div.style.top = ''; div.style.top = '';
@@ -679,23 +661,22 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() {
div.style.backgroundColor = ''; div.style.backgroundColor = '';
div.style.borderColor = ''; div.style.borderColor = '';
if (Blockly.DropDownDiv.onHide_) { if (DropDownDiv.onHide_) {
Blockly.DropDownDiv.onHide_(); DropDownDiv.onHide_();
Blockly.DropDownDiv.onHide_ = null; DropDownDiv.onHide_ = null;
} }
Blockly.DropDownDiv.clearContent(); DropDownDiv.clearContent();
Blockly.DropDownDiv.owner_ = null; DropDownDiv.owner_ = null;
if (Blockly.DropDownDiv.rendererClassName_) { if (DropDownDiv.rendererClassName_) {
Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.rendererClassName_); dom.removeClass(div, DropDownDiv.rendererClassName_);
Blockly.DropDownDiv.rendererClassName_ = ''; DropDownDiv.rendererClassName_ = '';
} }
if (Blockly.DropDownDiv.themeClassName_) { if (DropDownDiv.themeClassName_) {
Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.themeClassName_); dom.removeClass(div, DropDownDiv.themeClassName_);
Blockly.DropDownDiv.themeClassName_ = ''; DropDownDiv.themeClassName_ = '';
} }
(/** @type {!Blockly.WorkspaceSvg} */ ( (/** @type {!WorkspaceSvg} */ (common.getMainWorkspace())).markFocused();
Blockly.common.getMainWorkspace())).markFocused();
}; };
/** /**
@@ -707,31 +688,30 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() {
* @param {number} secondaryY Secondary/alternative origin point y, * @param {number} secondaryY Secondary/alternative origin point y,
* in absolute px. * in absolute px.
* @return {boolean} True if the menu rendered at the primary origin point. * @return {boolean} True if the menu rendered at the primary origin point.
* @private
*/ */
Blockly.DropDownDiv.positionInternal_ = function( const positionInternal = function(primaryX, primaryY, secondaryX, secondaryY) {
primaryX, primaryY, secondaryX, secondaryY) { const metrics =
var metrics = Blockly.DropDownDiv.getPositionMetrics_(primaryX, primaryY, internal.getPositionMetrics(primaryX, primaryY, secondaryX, secondaryY);
secondaryX, secondaryY);
// Update arrow CSS. // Update arrow CSS.
if (metrics.arrowVisible) { if (metrics.arrowVisible) {
Blockly.DropDownDiv.arrow_.style.display = ''; DropDownDiv.arrow_.style.display = '';
Blockly.DropDownDiv.arrow_.style.transform = 'translate(' + DropDownDiv.arrow_.style.transform = 'translate(' + metrics.arrowX + 'px,' +
metrics.arrowX + 'px,' + metrics.arrowY + 'px) rotate(45deg)'; metrics.arrowY + 'px) rotate(45deg)';
Blockly.DropDownDiv.arrow_.setAttribute('class', metrics.arrowAtTop ? DropDownDiv.arrow_.setAttribute(
'blocklyDropDownArrow blocklyArrowTop' : 'class',
'blocklyDropDownArrow blocklyArrowBottom'); metrics.arrowAtTop ? 'blocklyDropDownArrow blocklyArrowTop' :
'blocklyDropDownArrow blocklyArrowBottom');
} else { } else {
Blockly.DropDownDiv.arrow_.style.display = 'none'; DropDownDiv.arrow_.style.display = 'none';
} }
var initialX = Math.floor(metrics.initialX); const initialX = Math.floor(metrics.initialX);
var initialY = Math.floor(metrics.initialY); const initialY = Math.floor(metrics.initialY);
var finalX = Math.floor(metrics.finalX); const finalX = Math.floor(metrics.finalX);
var finalY = Math.floor(metrics.finalY); const finalY = Math.floor(metrics.finalY);
var div = Blockly.DropDownDiv.DIV_; const div = DropDownDiv.DIV_;
// First apply initial translation. // First apply initial translation.
div.style.left = initialX + 'px'; div.style.left = initialX + 'px';
div.style.top = initialY + 'px'; div.style.top = initialY + 'px';
@@ -742,8 +722,8 @@ Blockly.DropDownDiv.positionInternal_ = function(
// Add final translate, animated through `transition`. // Add final translate, animated through `transition`.
// Coordinates are relative to (initialX, initialY), // Coordinates are relative to (initialX, initialY),
// where the drop-down is absolutely positioned. // where the drop-down is absolutely positioned.
var dx = finalX - initialX; const dx = finalX - initialX;
var dy = finalY - initialY; const dy = finalY - initialY;
div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)'; div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
return !!metrics.arrowAtTop; return !!metrics.arrowAtTop;
@@ -754,27 +734,28 @@ Blockly.DropDownDiv.positionInternal_ = function(
* calculate the new position, it will just hide it instead. * calculate the new position, it will just hide it instead.
* @package * @package
*/ */
Blockly.DropDownDiv.repositionForWindowResize = function() { DropDownDiv.repositionForWindowResize = function() {
// This condition mainly catches the dropdown div when it is being used as a // This condition mainly catches the dropdown div when it is being used as a
// dropdown. It is important not to close it in this case because on Android, // dropdown. It is important not to close it in this case because on Android,
// when a field is focused, the soft keyboard opens triggering a window resize // when a field is focused, the soft keyboard opens triggering a window resize
// event and we want the dropdown div to stick around so users can type into // event and we want the dropdown div to stick around so users can type into
// it. // it.
if (Blockly.DropDownDiv.owner_) { if (DropDownDiv.owner_) {
var field = /** @type {!Blockly.Field} */ (Blockly.DropDownDiv.owner_); const field = /** @type {!Field} */ (DropDownDiv.owner_);
var block = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock()); const block = /** @type {!BlockSvg} */ (field.getSourceBlock());
var bBox = Blockly.DropDownDiv.positionToField_ ? const bBox = DropDownDiv.positionToField_ ? getScaledBboxOfField(field) :
Blockly.DropDownDiv.getScaledBboxOfField_(field) : getScaledBboxOfBlock(block);
Blockly.DropDownDiv.getScaledBboxOfBlock_(block);
// If we can fit it, render below the block. // If we can fit it, render below the block.
var primaryX = bBox.left + (bBox.right - bBox.left) / 2; const primaryX = bBox.left + (bBox.right - bBox.left) / 2;
var primaryY = bBox.bottom; const primaryY = bBox.bottom;
// If we can't fit it, render above the entire parent block. // If we can't fit it, render above the entire parent block.
var secondaryX = primaryX; const secondaryX = primaryX;
var secondaryY = bBox.top; const secondaryY = bBox.top;
Blockly.DropDownDiv.positionInternal_( positionInternal(primaryX, primaryY, secondaryX, secondaryY);
primaryX, primaryY, secondaryX, secondaryY);
} else { } else {
Blockly.DropDownDiv.hide(); DropDownDiv.hide();
} }
}; };
exports = DropDownDiv;
exports.TEST_ONLY = internal;

View File

@@ -33,7 +33,7 @@ goog.addDependency('../../core/contextmenu_registry.js', ['Blockly.ContextMenuRe
goog.addDependency('../../core/css.js', ['Blockly.Css'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/css.js', ['Blockly.Css'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/delete_area.js', ['Blockly.DeleteArea'], ['Blockly.BlockSvg', 'Blockly.DragTarget', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/delete_area.js', ['Blockly.DeleteArea'], ['Blockly.BlockSvg', 'Blockly.DragTarget', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/drag_target.js', ['Blockly.DragTarget'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/drag_target.js', ['Blockly.DragTarget'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/dropdowndiv.js', ['Blockly.DropDownDiv'], ['Blockly.common', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.style']); goog.addDependency('../../core/dropdowndiv.js', ['Blockly.DropDownDiv'], ['Blockly.common', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.style'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/events/events.js', ['Blockly.Events'], ['Blockly.registry', 'Blockly.utils']); goog.addDependency('../../core/events/events.js', ['Blockly.Events'], ['Blockly.registry', 'Blockly.utils']);
goog.addDependency('../../core/events/events_abstract.js', ['Blockly.Events.Abstract'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_abstract.js', ['Blockly.Events.Abstract'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/events/events_block_base.js', ['Blockly.Events.BlockBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/events/events_block_base.js', ['Blockly.Events.BlockBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});

View File

@@ -4,11 +4,13 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
const DropDownDiv = goog.require('Blockly.DropDownDiv');
suite('DropDownDiv', function() { suite('DropDownDiv', function() {
suite('Positioning', function() { suite('Positioning', function() {
setup(function() { setup(function() {
sharedTestSetup.call(this); sharedTestSetup.call(this);
this.boundsStub = sinon.stub(Blockly.DropDownDiv, 'getBoundsInfo_') this.boundsStub = sinon.stub(Blockly.DropDownDiv.TEST_ONLY, 'getBoundsInfo')
.returns({ .returns({
left: 0, left: 0,
right: 100, right: 100,
@@ -31,7 +33,7 @@ suite('DropDownDiv', function() {
sharedTestTeardown.call(this); sharedTestTeardown.call(this);
}); });
test('Below, in Bounds', function() { test('Below, in Bounds', function() {
var metrics = Blockly.DropDownDiv.getPositionMetrics_(50, 0, 50, -10); var metrics = Blockly.DropDownDiv.TEST_ONLY.getPositionMetrics(50, 0, 50, -10);
// "Above" in value actually means below in render. // "Above" in value actually means below in render.
chai.assert.isAtLeast(metrics.initialY, 0); chai.assert.isAtLeast(metrics.initialY, 0);
chai.assert.isAbove(metrics.finalY, 0); chai.assert.isAbove(metrics.finalY, 0);
@@ -39,7 +41,7 @@ suite('DropDownDiv', function() {
chai.assert.isTrue(metrics.arrowAtTop); chai.assert.isTrue(metrics.arrowAtTop);
}); });
test('Above, in Bounds', function() { test('Above, in Bounds', function() {
var metrics = Blockly.DropDownDiv.getPositionMetrics_(50, 100, 50, 90); var metrics = Blockly.DropDownDiv.TEST_ONLY.getPositionMetrics(50, 100, 50, 90);
// "Below" in value actually means above in render. // "Below" in value actually means above in render.
chai.assert.isAtMost(metrics.initialY, 100); chai.assert.isAtMost(metrics.initialY, 100);
chai.assert.isBelow(metrics.finalY, 100); chai.assert.isBelow(metrics.finalY, 100);
@@ -47,7 +49,7 @@ suite('DropDownDiv', function() {
chai.assert.isFalse(metrics.arrowAtTop); chai.assert.isFalse(metrics.arrowAtTop);
}); });
test('Below, out of Bounds', function() { test('Below, out of Bounds', function() {
var metrics = Blockly.DropDownDiv.getPositionMetrics_(50, 60, 50, 50); var metrics = Blockly.DropDownDiv.TEST_ONLY.getPositionMetrics(50, 60, 50, 50);
// "Above" in value actually means below in render. // "Above" in value actually means below in render.
chai.assert.isAtLeast(metrics.initialY, 60); chai.assert.isAtLeast(metrics.initialY, 60);
chai.assert.isAbove(metrics.finalY, 60); chai.assert.isAbove(metrics.finalY, 60);
@@ -55,7 +57,7 @@ suite('DropDownDiv', function() {
chai.assert.isTrue(metrics.arrowAtTop); chai.assert.isTrue(metrics.arrowAtTop);
}); });
test('Above, in Bounds', function() { test('Above, in Bounds', function() {
var metrics = Blockly.DropDownDiv.getPositionMetrics_(50, 100, 50, 90); var metrics = Blockly.DropDownDiv.TEST_ONLY.getPositionMetrics(50, 100, 50, 90);
// "Below" in value actually means above in render. // "Below" in value actually means above in render.
chai.assert.isAtMost(metrics.initialY, 100); chai.assert.isAtMost(metrics.initialY, 100);
chai.assert.isBelow(metrics.finalY, 100); chai.assert.isBelow(metrics.finalY, 100);
@@ -64,7 +66,7 @@ suite('DropDownDiv', function() {
}); });
test('No Solution, Render At Top', function() { test('No Solution, Render At Top', function() {
this.clientHeightStub.get(function() { return 100; }); this.clientHeightStub.get(function() { return 100; });
var metrics = Blockly.DropDownDiv.getPositionMetrics_(50, 60, 50, 50); var metrics = Blockly.DropDownDiv.TEST_ONLY.getPositionMetrics(50, 60, 50, 50);
// "Above" in value actually means below in render. // "Above" in value actually means below in render.
chai.assert.equal(metrics.initialY, 0); chai.assert.equal(metrics.initialY, 0);
chai.assert.equal(metrics.finalY, 0); chai.assert.equal(metrics.finalY, 0);