mirror of
https://github.com/google/blockly.git
synced 2026-01-08 01:20:12 +01:00
Merge pull request #5207 from alschmiedt/migrate_dropdown
Migrate dropdown to goog.module syntax
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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'});
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user