mirror of
https://github.com/google/blockly.git
synced 2026-01-06 08:30:13 +01:00
chore: Reduce delta on ports to blockly-samples (#6886)
* Reduce usage of obsolete .keyCode property.
* Rename private properties/methods which violate eslint rules.
* Use arrays of bound events rather than individual properties.
* Improve typing info.
* Also fix O(n^2) recursive performance issue in theme's getComponentStyle function.
* And replace String(...) with '${...}' (smaller, faster).
* .toString() is considered harmful.
This commit is contained in:
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -5,7 +5,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: >
|
value: >
|
||||||
Thank you for taking the time to fill out a bug report!
|
Thank you for taking the time to fill out a bug report!
|
||||||
If you have a question about how to use Blockly in your application,
|
If you have a question about how to use Blockly in your application,
|
||||||
please ask on the [forum](https://groups.google.com/forum/#!forum/blockly) instead of filing an issue.
|
please ask on the [forum](https://groups.google.com/forum/#!forum/blockly) instead of filing an issue.
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@@ -6,7 +6,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: >
|
value: >
|
||||||
Thank you for taking the time to fill out a feature request!
|
Thank you for taking the time to fill out a feature request!
|
||||||
If you have a question about how to use Blockly in your application,
|
If you have a question about how to use Blockly in your application,
|
||||||
please ask on the [forum](https://groups.google.com/forum/#!forum/blockly) instead of filing an issue.
|
please ask on the [forum](https://groups.google.com/forum/#!forum/blockly) instead of filing an issue.
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ function connectionUiStep(ripple: SVGElement, start: Date, scale: number) {
|
|||||||
if (percent > 1) {
|
if (percent > 1) {
|
||||||
dom.removeNode(ripple);
|
dom.removeNode(ripple);
|
||||||
} else {
|
} else {
|
||||||
ripple.setAttribute('r', (percent * 25 * scale).toString());
|
ripple.setAttribute('r', String(percent * 25 * scale));
|
||||||
ripple.style.opacity = (1 - percent).toString();
|
ripple.style.opacity = String(1 - percent);
|
||||||
disconnectPid = setTimeout(connectionUiStep, 10, ripple, start, scale);
|
disconnectPid = setTimeout(connectionUiStep, 10, ripple, start, scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,10 +145,7 @@ export function unbind(bindData: Data): (e: Event) => void {
|
|||||||
// should only pass Data from bind or conditionalBind.
|
// should only pass Data from bind or conditionalBind.
|
||||||
const callback = bindData[bindData.length - 1][2];
|
const callback = bindData[bindData.length - 1][2];
|
||||||
while (bindData.length) {
|
while (bindData.length) {
|
||||||
const bindDatum = bindData.pop();
|
const [node, name, func] = bindData.pop()!;
|
||||||
const node = bindDatum![0];
|
|
||||||
const name = bindDatum![1];
|
|
||||||
const func = bindDatum![2];
|
|
||||||
node.removeEventListener(name, func, false);
|
node.removeEventListener(name, func, false);
|
||||||
}
|
}
|
||||||
return callback;
|
return callback;
|
||||||
|
|||||||
@@ -218,27 +218,26 @@ export class Bubble implements IBubble {
|
|||||||
'blocklyResizeSE',
|
'blocklyResizeSE',
|
||||||
},
|
},
|
||||||
this.bubbleGroup);
|
this.bubbleGroup);
|
||||||
const resizeSize = 2 * Bubble.BORDER_WIDTH;
|
const size = 2 * Bubble.BORDER_WIDTH;
|
||||||
dom.createSvgElement(
|
dom.createSvgElement(
|
||||||
Svg.POLYGON,
|
Svg.POLYGON, {'points': `0,${size} ${size},${size} ${size},0`},
|
||||||
{'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())},
|
|
||||||
this.resizeGroup);
|
this.resizeGroup);
|
||||||
dom.createSvgElement(
|
dom.createSvgElement(
|
||||||
Svg.LINE, {
|
Svg.LINE, {
|
||||||
'class': 'blocklyResizeLine',
|
'class': 'blocklyResizeLine',
|
||||||
'x1': resizeSize / 3,
|
'x1': size / 3,
|
||||||
'y1': resizeSize - 1,
|
'y1': size - 1,
|
||||||
'x2': resizeSize - 1,
|
'x2': size - 1,
|
||||||
'y2': resizeSize / 3,
|
'y2': size / 3,
|
||||||
},
|
},
|
||||||
this.resizeGroup);
|
this.resizeGroup);
|
||||||
dom.createSvgElement(
|
dom.createSvgElement(
|
||||||
Svg.LINE, {
|
Svg.LINE, {
|
||||||
'class': 'blocklyResizeLine',
|
'class': 'blocklyResizeLine',
|
||||||
'x1': resizeSize * 2 / 3,
|
'x1': size * 2 / 3,
|
||||||
'y1': resizeSize - 1,
|
'y1': size - 1,
|
||||||
'x2': resizeSize - 1,
|
'x2': size - 1,
|
||||||
'y2': resizeSize * 2 / 3,
|
'y2': size * 2 / 3,
|
||||||
},
|
},
|
||||||
this.resizeGroup);
|
this.resizeGroup);
|
||||||
} else {
|
} else {
|
||||||
@@ -660,8 +659,8 @@ export class Bubble implements IBubble {
|
|||||||
height = Math.max(height, doubleBorderWidth + 20);
|
height = Math.max(height, doubleBorderWidth + 20);
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.bubbleBack?.setAttribute('width', width.toString());
|
this.bubbleBack?.setAttribute('width', `${width}`);
|
||||||
this.bubbleBack?.setAttribute('height', height.toString());
|
this.bubbleBack?.setAttribute('height', `${height}`);
|
||||||
if (this.resizeGroup) {
|
if (this.resizeGroup) {
|
||||||
if (this.workspace_.RTL) {
|
if (this.workspace_.RTL) {
|
||||||
// Mirror the resize group.
|
// Mirror the resize group.
|
||||||
@@ -901,8 +900,7 @@ export class Bubble implements IBubble {
|
|||||||
textElement = paragraphElement.childNodes[i] as SVGTSpanElement;
|
textElement = paragraphElement.childNodes[i] as SVGTSpanElement;
|
||||||
i++) {
|
i++) {
|
||||||
textElement.setAttribute('text-anchor', 'end');
|
textElement.setAttribute('text-anchor', 'end');
|
||||||
textElement.setAttribute(
|
textElement.setAttribute('x', String(maxWidth + Bubble.BORDER_WIDTH));
|
||||||
'x', (maxWidth + Bubble.BORDER_WIDTH).toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bubble;
|
return bubble;
|
||||||
|
|||||||
@@ -42,17 +42,12 @@ export class Comment extends Icon {
|
|||||||
*/
|
*/
|
||||||
private cachedText: string|null = '';
|
private cachedText: string|null = '';
|
||||||
|
|
||||||
/** Mouse up event data. */
|
/**
|
||||||
private onMouseUpWrapper: browserEvents.Data|null = null;
|
* Array holding info needed to unbind events.
|
||||||
|
* Used for disposing.
|
||||||
/** Wheel event data. */
|
* Ex: [[node, name, func], [node, name, func]].
|
||||||
private onWheelWrapper: browserEvents.Data|null = null;
|
*/
|
||||||
|
private boundEvents: browserEvents.Data[] = [];
|
||||||
/** Change event data. */
|
|
||||||
private onChangeWrapper: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** Input event data. */
|
|
||||||
private onInputWrapper: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SVG element that contains the text edit area, or null if not created.
|
* The SVG element that contains the text edit area, or null if not created.
|
||||||
@@ -147,14 +142,14 @@ export class Comment extends Icon {
|
|||||||
body.appendChild(textarea);
|
body.appendChild(textarea);
|
||||||
this.foreignObject!.appendChild(body);
|
this.foreignObject!.appendChild(body);
|
||||||
|
|
||||||
this.onMouseUpWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
textarea, 'focus', this, this.startEdit, true);
|
textarea, 'focus', this, this.startEdit, true));
|
||||||
// Don't zoom with mousewheel.
|
// Don't zoom with mousewheel.
|
||||||
this.onWheelWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
textarea, 'wheel', this, function(e: Event) {
|
textarea, 'wheel', this, function(e: Event) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
}));
|
||||||
this.onChangeWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
textarea, 'change', this,
|
textarea, 'change', this,
|
||||||
/**
|
/**
|
||||||
* @param _e Unused event parameter.
|
* @param _e Unused event parameter.
|
||||||
@@ -165,15 +160,15 @@ export class Comment extends Icon {
|
|||||||
this.getBlock(), 'comment', null, this.cachedText,
|
this.getBlock(), 'comment', null, this.cachedText,
|
||||||
this.model.text));
|
this.model.text));
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
this.onInputWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
textarea, 'input', this,
|
textarea, 'input', this,
|
||||||
/**
|
/**
|
||||||
* @param _e Unused event parameter.
|
* @param _e Unused event parameter.
|
||||||
*/
|
*/
|
||||||
function(this: Comment, _e: Event) {
|
function(this: Comment, _e: Event) {
|
||||||
this.model.text = textarea.value;
|
this.model.text = textarea.value;
|
||||||
});
|
}));
|
||||||
|
|
||||||
setTimeout(textarea.focus.bind(textarea), 0);
|
setTimeout(textarea.focus.bind(textarea), 0);
|
||||||
|
|
||||||
@@ -277,22 +272,10 @@ export class Comment extends Icon {
|
|||||||
* Dispose of the bubble.
|
* Dispose of the bubble.
|
||||||
*/
|
*/
|
||||||
private disposeBubble() {
|
private disposeBubble() {
|
||||||
if (this.onMouseUpWrapper) {
|
for (const event of this.boundEvents) {
|
||||||
browserEvents.unbind(this.onMouseUpWrapper);
|
browserEvents.unbind(event);
|
||||||
this.onMouseUpWrapper = null;
|
|
||||||
}
|
|
||||||
if (this.onWheelWrapper) {
|
|
||||||
browserEvents.unbind(this.onWheelWrapper);
|
|
||||||
this.onWheelWrapper = null;
|
|
||||||
}
|
|
||||||
if (this.onChangeWrapper) {
|
|
||||||
browserEvents.unbind(this.onChangeWrapper);
|
|
||||||
this.onChangeWrapper = null;
|
|
||||||
}
|
|
||||||
if (this.onInputWrapper) {
|
|
||||||
browserEvents.unbind(this.onInputWrapper);
|
|
||||||
this.onInputWrapper = null;
|
|
||||||
}
|
}
|
||||||
|
this.boundEvents.length = 0;
|
||||||
if (this.bubble_) {
|
if (this.bubble_) {
|
||||||
this.bubble_.dispose();
|
this.bubble_.dispose();
|
||||||
this.bubble_ = null;
|
this.bubble_ = null;
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export class ComponentManager {
|
|||||||
'Plugin "' + id + 'already has capability "' + capability + '"');
|
'Plugin "' + id + 'already has capability "' + capability + '"');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
capability = String(capability).toLowerCase();
|
capability = `${capability}`.toLowerCase();
|
||||||
this.componentData.get(id)?.capabilities.push(capability);
|
this.componentData.get(id)?.capabilities.push(capability);
|
||||||
this.capabilityToComponentIds.get(capability)?.push(id);
|
this.capabilityToComponentIds.get(capability)?.push(id);
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ export class ComponentManager {
|
|||||||
'" to remove');
|
'" to remove');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
capability = String(capability).toLowerCase();
|
capability = `${capability}`.toLowerCase();
|
||||||
arrayUtils.removeElem(this.componentData.get(id)!.capabilities, capability);
|
arrayUtils.removeElem(this.componentData.get(id)!.capabilities, capability);
|
||||||
arrayUtils.removeElem(this.capabilityToComponentIds.get(capability)!, id);
|
arrayUtils.removeElem(this.capabilityToComponentIds.get(capability)!, id);
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ export class ComponentManager {
|
|||||||
* @returns Whether the component has the capability.
|
* @returns Whether the component has the capability.
|
||||||
*/
|
*/
|
||||||
hasCapability<T>(id: string, capability: string|Capability<T>): boolean {
|
hasCapability<T>(id: string, capability: string|Capability<T>): boolean {
|
||||||
capability = String(capability).toLowerCase();
|
capability = `${capability}`.toLowerCase();
|
||||||
return this.componentData.has(id) &&
|
return this.componentData.has(id) &&
|
||||||
this.componentData.get(id)!.capabilities.indexOf(capability) !== -1;
|
this.componentData.get(id)!.capabilities.indexOf(capability) !== -1;
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ export class ComponentManager {
|
|||||||
*/
|
*/
|
||||||
getComponents<T extends IComponent>(
|
getComponents<T extends IComponent>(
|
||||||
capability: string|Capability<T>, sorted: boolean): T[] {
|
capability: string|Capability<T>, sorted: boolean): T[] {
|
||||||
capability = String(capability).toLowerCase();
|
capability = `${capability}`.toLowerCase();
|
||||||
const componentIds = this.capabilityToComponentIds.get(capability);
|
const componentIds = this.capabilityToComponentIds.get(capability);
|
||||||
if (!componentIds) {
|
if (!componentIds) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -261,10 +261,8 @@ export function registerDeleteAll() {
|
|||||||
const deletableBlocksLength = getDeletableBlocks_(scope.workspace).length;
|
const deletableBlocksLength = getDeletableBlocks_(scope.workspace).length;
|
||||||
if (deletableBlocksLength === 1) {
|
if (deletableBlocksLength === 1) {
|
||||||
return Msg['DELETE_BLOCK'];
|
return Msg['DELETE_BLOCK'];
|
||||||
} else {
|
|
||||||
return Msg['DELETE_X_BLOCKS'].replace(
|
|
||||||
'%1', String(deletableBlocksLength));
|
|
||||||
}
|
}
|
||||||
|
return Msg['DELETE_X_BLOCKS'].replace('%1', `${deletableBlocksLength}`);
|
||||||
},
|
},
|
||||||
preconditionFn(scope: Scope) {
|
preconditionFn(scope: Scope) {
|
||||||
if (!scope.workspace) {
|
if (!scope.workspace) {
|
||||||
@@ -489,7 +487,7 @@ export function registerDelete() {
|
|||||||
}
|
}
|
||||||
return descendantCount === 1 ?
|
return descendantCount === 1 ?
|
||||||
Msg['DELETE_BLOCK'] :
|
Msg['DELETE_BLOCK'] :
|
||||||
Msg['DELETE_X_BLOCKS'].replace('%1', String(descendantCount));
|
Msg['DELETE_X_BLOCKS'].replace('%1', `${descendantCount}`);
|
||||||
},
|
},
|
||||||
preconditionFn(scope: Scope) {
|
preconditionFn(scope: Scope) {
|
||||||
if (!scope.block!.isInFlyout && scope.block!.isDeletable()) {
|
if (!scope.block!.isInFlyout && scope.block!.isDeletable()) {
|
||||||
|
|||||||
@@ -203,16 +203,16 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
* Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by
|
* Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by
|
||||||
* subclasses that want to handle configuration and setting the field value
|
* subclasses that want to handle configuration and setting the field value
|
||||||
* after their own constructors have run).
|
* after their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a value & returns a validated value, or null to
|
* field's value. Takes in a value & returns a validated value, or null to
|
||||||
* abort the change.
|
* abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* Refer to the individual field's documentation for a list of properties
|
* Refer to the individual field's documentation for a list of properties
|
||||||
* this parameter supports.
|
* this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
value: T|Sentinel, opt_validator?: FieldValidator<T>|null,
|
value: T|Sentinel, validator?: FieldValidator<T>|null,
|
||||||
opt_config?: FieldConfig) {
|
config?: FieldConfig) {
|
||||||
/**
|
/**
|
||||||
* A generic value possessed by the field.
|
* A generic value possessed by the field.
|
||||||
* Should generally be non-null, only null when the field is created.
|
* Should generally be non-null, only null when the field is created.
|
||||||
@@ -225,12 +225,12 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
this.size_ = new Size(0, 0);
|
this.size_ = new Size(0, 0);
|
||||||
|
|
||||||
if (Field.isSentinel(value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,14 +715,14 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
* Calls showEditor_ when the field is clicked if the field is clickable.
|
* Calls showEditor_ when the field is clicked if the field is clickable.
|
||||||
* Do not override.
|
* Do not override.
|
||||||
*
|
*
|
||||||
* @param opt_e Optional mouse event that triggered the field to open, or
|
* @param e Optional mouse event that triggered the field to open, or
|
||||||
* undefined if triggered programmatically.
|
* undefined if triggered programmatically.
|
||||||
* @sealed
|
* @sealed
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
showEditor(opt_e?: Event) {
|
showEditor(e?: Event) {
|
||||||
if (this.isClickable()) {
|
if (this.isClickable()) {
|
||||||
this.showEditor_(opt_e);
|
this.showEditor_(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,11 +739,11 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
/**
|
/**
|
||||||
* Updates the size of the field based on the text.
|
* Updates the size of the field based on the text.
|
||||||
*
|
*
|
||||||
* @param opt_margin margin to use when positioning the text element.
|
* @param margin margin to use when positioning the text element.
|
||||||
*/
|
*/
|
||||||
protected updateSize_(opt_margin?: number) {
|
protected updateSize_(margin?: number) {
|
||||||
const constants = this.getConstants();
|
const constants = this.getConstants();
|
||||||
const xOffset = opt_margin !== undefined ? opt_margin :
|
const xOffset = margin !== undefined ? margin :
|
||||||
this.borderRect_ ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING :
|
this.borderRect_ ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING :
|
||||||
0;
|
0;
|
||||||
let totalWidth = xOffset * 2;
|
let totalWidth = xOffset * 2;
|
||||||
@@ -783,17 +783,17 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
|
|
||||||
this.textElement_.setAttribute(
|
this.textElement_.setAttribute(
|
||||||
'x',
|
'x',
|
||||||
`${
|
String(
|
||||||
this.getSourceBlock()?.RTL ?
|
this.getSourceBlock()?.RTL ?
|
||||||
this.size_.width - contentWidth - xOffset :
|
this.size_.width - contentWidth - xOffset :
|
||||||
xOffset}`);
|
xOffset));
|
||||||
this.textElement_.setAttribute(
|
this.textElement_.setAttribute(
|
||||||
'y',
|
'y',
|
||||||
`${
|
String(
|
||||||
constants!.FIELD_TEXT_BASELINE_CENTER ?
|
constants!.FIELD_TEXT_BASELINE_CENTER ?
|
||||||
halfHeight :
|
halfHeight :
|
||||||
halfHeight - constants!.FIELD_TEXT_HEIGHT / 2 +
|
halfHeight - constants!.FIELD_TEXT_HEIGHT / 2 +
|
||||||
constants!.FIELD_TEXT_BASELINE}`);
|
constants!.FIELD_TEXT_BASELINE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Position a field's border rect after a size change. */
|
/** Position a field's border rect after a size change. */
|
||||||
@@ -801,12 +801,12 @@ export abstract class Field<T = any> implements IASTNodeLocationSvg,
|
|||||||
if (!this.borderRect_) {
|
if (!this.borderRect_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.borderRect_.setAttribute('width', `${this.size_.width}`);
|
this.borderRect_.setAttribute('width', String(this.size_.width));
|
||||||
this.borderRect_.setAttribute('height', `${this.size_.height}`);
|
this.borderRect_.setAttribute('height', String(this.size_.height));
|
||||||
this.borderRect_.setAttribute(
|
this.borderRect_.setAttribute(
|
||||||
'rx', `${this.getConstants()!.FIELD_BORDER_RECT_RADIUS}`);
|
'rx', String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS));
|
||||||
this.borderRect_.setAttribute(
|
this.borderRect_.setAttribute(
|
||||||
'ry', `${this.getConstants()!.FIELD_BORDER_RECT_RADIUS}`);
|
'ry', String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {Field, UnattachedFieldError} from './field.js';
|
|||||||
import * as fieldRegistry from './field_registry.js';
|
import * as fieldRegistry from './field_registry.js';
|
||||||
import {FieldInput, FieldInputConfig, FieldInputValidator} from './field_input.js';
|
import {FieldInput, FieldInputConfig, FieldInputValidator} from './field_input.js';
|
||||||
import * as dom from './utils/dom.js';
|
import * as dom from './utils/dom.js';
|
||||||
import {KeyCodes} from './utils/keycodes.js';
|
|
||||||
import * as math from './utils/math.js';
|
import * as math from './utils/math.js';
|
||||||
import type {Sentinel} from './utils/sentinel.js';
|
import type {Sentinel} from './utils/sentinel.js';
|
||||||
import {Svg} from './utils/svg.js';
|
import {Svg} from './utils/svg.js';
|
||||||
@@ -31,15 +30,15 @@ import * as WidgetDiv from './widgetdiv.js';
|
|||||||
* Class for an editable angle field.
|
* Class for an editable angle field.
|
||||||
*/
|
*/
|
||||||
export class FieldAngle extends FieldInput<number> {
|
export class FieldAngle extends FieldInput<number> {
|
||||||
/**
|
|
||||||
* The default amount to round angles to when using a mouse or keyboard nav
|
|
||||||
* input. Must be a positive integer to support keyboard navigation.
|
|
||||||
*/
|
|
||||||
static readonly ROUND = 15;
|
|
||||||
|
|
||||||
/** Half the width of protractor image. */
|
/** Half the width of protractor image. */
|
||||||
static readonly HALF = 100 / 2;
|
static readonly HALF = 100 / 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Radius of protractor circle. Slightly smaller than protractor size since
|
||||||
|
* otherwise SVG crops off half the border at the edges.
|
||||||
|
*/
|
||||||
|
static readonly RADIUS: number = FieldAngle.HALF - 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default property describing which direction makes an angle field's value
|
* Default property describing which direction makes an angle field's value
|
||||||
* increase. Angle increases clockwise (true) or counterclockwise (false).
|
* increase. Angle increases clockwise (true) or counterclockwise (false).
|
||||||
@@ -60,84 +59,73 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
static readonly WRAP = 360;
|
static readonly WRAP = 360;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Radius of protractor circle. Slightly smaller than protractor size since
|
* The default amount to round angles to when using a mouse or keyboard nav
|
||||||
* otherwise SVG crops off half the border at the edges.
|
* input. Must be a positive integer to support keyboard navigation.
|
||||||
*/
|
*/
|
||||||
static readonly RADIUS: number = FieldAngle.HALF - 1;
|
static readonly ROUND = 15;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the angle should increase as the angle picker is moved clockwise
|
* Whether the angle should increase as the angle picker is moved clockwise
|
||||||
* (true) or counterclockwise (false).
|
* (true) or counterclockwise (false).
|
||||||
*/
|
*/
|
||||||
private clockwise_ = FieldAngle.CLOCKWISE;
|
private clockwise = FieldAngle.CLOCKWISE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The offset of zero degrees (and all other angles).
|
* The offset of zero degrees (and all other angles).
|
||||||
*/
|
*/
|
||||||
private offset_ = FieldAngle.OFFSET;
|
private offset = FieldAngle.OFFSET;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum angle to allow before wrapping.
|
* The maximum angle to allow before wrapping.
|
||||||
*/
|
*/
|
||||||
private wrap_ = FieldAngle.WRAP;
|
private wrap = FieldAngle.WRAP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount to round angles to when using a mouse or keyboard nav input.
|
* The amount to round angles to when using a mouse or keyboard nav input.
|
||||||
*/
|
*/
|
||||||
private round_ = FieldAngle.ROUND;
|
private round = FieldAngle.ROUND;
|
||||||
|
|
||||||
/** The angle picker's SVG element. */
|
/**
|
||||||
private editor_: SVGSVGElement|null = null;
|
* Array holding info needed to unbind events.
|
||||||
|
* Used for disposing.
|
||||||
|
* Ex: [[node, name, func], [node, name, func]].
|
||||||
|
*/
|
||||||
|
private boundEvents: browserEvents.Data[] = [];
|
||||||
|
|
||||||
/** The angle picker's gauge path depending on the value. */
|
/** Dynamic red line pointing at the value's angle. */
|
||||||
gauge_: SVGPathElement|null = null;
|
private line: SVGLineElement|null = null;
|
||||||
|
|
||||||
/** The angle picker's line drawn representing the value's angle. */
|
/** Dynamic pink area extending from 0 to the value's angle. */
|
||||||
line_: SVGLineElement|null = null;
|
private gauge: SVGPathElement|null = null;
|
||||||
|
|
||||||
/** The degree symbol for this field. */
|
/** The degree symbol for this field. */
|
||||||
protected symbol_: SVGTSpanElement|null = null;
|
protected symbol_: SVGTSpanElement|null = null;
|
||||||
|
|
||||||
/** Wrapper click event data. */
|
|
||||||
private clickWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** Surface click event data. */
|
|
||||||
private clickSurfaceWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** Surface mouse move event data. */
|
|
||||||
private moveSurfaceWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializable fields are saved by the serializer, non-serializable fields
|
* @param value The initial value of the field. Should cast to a number.
|
||||||
* are not. Editable fields should also be serializable.
|
|
||||||
*/
|
|
||||||
override SERIALIZABLE = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param opt_value The initial value of the field. Should cast to a number.
|
|
||||||
* Defaults to 0. Also accepts Field.SKIP_SETUP if you wish to skip setup
|
* Defaults to 0. Also accepts Field.SKIP_SETUP if you wish to skip setup
|
||||||
* (only used by subclasses that want to handle configuration and setting
|
* (only used by subclasses that want to handle configuration and setting
|
||||||
* the field value after their own constructors have run).
|
* the field value after their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a number & returns a validated number, or null
|
* field's value. Takes in a number & returns a validated number, or null
|
||||||
* to abort the change.
|
* to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/angle#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/angle#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|number|Sentinel, opt_validator?: FieldAngleValidator,
|
value?: string|number|Sentinel, validator?: FieldAngleValidator,
|
||||||
opt_config?: FieldAngleConfig) {
|
config?: FieldAngleConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,22 +139,22 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
|
|
||||||
switch (config.mode) {
|
switch (config.mode) {
|
||||||
case Mode.COMPASS:
|
case Mode.COMPASS:
|
||||||
this.clockwise_ = true;
|
this.clockwise = true;
|
||||||
this.offset_ = 90;
|
this.offset = 90;
|
||||||
break;
|
break;
|
||||||
case Mode.PROTRACTOR:
|
case Mode.PROTRACTOR:
|
||||||
// This is the default mode, so we could do nothing. But just to
|
// This is the default mode, so we could do nothing. But just to
|
||||||
// future-proof, we'll set it anyway.
|
// future-proof, we'll set it anyway.
|
||||||
this.clockwise_ = false;
|
this.clockwise = false;
|
||||||
this.offset_ = 0;
|
this.offset = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow individual settings to override the mode setting.
|
// Allow individual settings to override the mode setting.
|
||||||
if (config.clockwise) this.clockwise_ = config.clockwise;
|
if (config.clockwise) this.clockwise = config.clockwise;
|
||||||
if (config.offset) this.offset_ = config.offset;
|
if (config.offset) this.offset = config.offset;
|
||||||
if (config.wrap) this.wrap_ = config.wrap;
|
if (config.wrap) this.wrap = config.wrap;
|
||||||
if (config.round) this.round_ = config.round;
|
if (config.round) this.round = config.round;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,32 +164,32 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
*/
|
*/
|
||||||
override initView() {
|
override initView() {
|
||||||
super.initView();
|
super.initView();
|
||||||
// Add the degree symbol to the left of the number, even in RTL (issue
|
// Add the degree symbol to the left of the number,
|
||||||
// #2380)
|
// even in RTL (issue #2380).
|
||||||
this.symbol_ = dom.createSvgElement(Svg.TSPAN, {});
|
this.symbol_ = dom.createSvgElement(Svg.TSPAN, {});
|
||||||
this.symbol_.appendChild(document.createTextNode('°'));
|
this.symbol_.appendChild(document.createTextNode('°'));
|
||||||
this.getTextElement().appendChild(this.symbol_);
|
this.getTextElement().appendChild(this.symbol_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates the graph when the field rerenders. */
|
/** Updates the angle when the field rerenders. */
|
||||||
protected override render_() {
|
protected override render_() {
|
||||||
super.render_();
|
super.render_();
|
||||||
this.updateGraph_();
|
this.updateGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and show the angle field's editor.
|
* Create and show the angle field's editor.
|
||||||
*
|
*
|
||||||
* @param opt_e Optional mouse event that triggered the field to open, or
|
* @param e Optional mouse event that triggered the field to open,
|
||||||
* undefined if triggered programmatically.
|
* or undefined if triggered programmatically.
|
||||||
*/
|
*/
|
||||||
protected override showEditor_(opt_e?: Event) {
|
protected override showEditor_(e?: Event) {
|
||||||
// Mobile browsers have issues with in-line textareas (focus & keyboards).
|
// Mobile browsers have issues with in-line textareas (focus & keyboards).
|
||||||
const noFocus = userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD;
|
const noFocus = userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD;
|
||||||
super.showEditor_(opt_e, noFocus);
|
super.showEditor_(e, noFocus);
|
||||||
|
|
||||||
this.dropdownCreate_();
|
const editor = this.dropdownCreate();
|
||||||
dropDownDiv.getContentDiv().appendChild(this.editor_!);
|
dropDownDiv.getContentDiv().appendChild(editor);
|
||||||
|
|
||||||
if (this.sourceBlock_ instanceof BlockSvg) {
|
if (this.sourceBlock_ instanceof BlockSvg) {
|
||||||
dropDownDiv.setColour(
|
dropDownDiv.setColour(
|
||||||
@@ -209,13 +197,17 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
this.sourceBlock_.style.colourTertiary);
|
this.sourceBlock_.style.colourTertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
dropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
|
dropDownDiv.showPositionedByField(this, this.dropdownDispose.bind(this));
|
||||||
|
|
||||||
this.updateGraph_();
|
this.updateGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create the angle dropdown editor. */
|
/**
|
||||||
private dropdownCreate_() {
|
* Creates the angle dropdown editor.
|
||||||
|
*
|
||||||
|
* @returns The newly created slider.
|
||||||
|
*/
|
||||||
|
private dropdownCreate(): SVGSVGElement {
|
||||||
const svg = dom.createSvgElement(Svg.SVG, {
|
const svg = dom.createSvgElement(Svg.SVG, {
|
||||||
'xmlns': dom.SVG_NS,
|
'xmlns': dom.SVG_NS,
|
||||||
'xmlns:html': dom.HTML_NS,
|
'xmlns:html': dom.HTML_NS,
|
||||||
@@ -233,9 +225,9 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
'class': 'blocklyAngleCircle',
|
'class': 'blocklyAngleCircle',
|
||||||
},
|
},
|
||||||
svg);
|
svg);
|
||||||
this.gauge_ =
|
this.gauge =
|
||||||
dom.createSvgElement(Svg.PATH, {'class': 'blocklyAngleGauge'}, svg);
|
dom.createSvgElement(Svg.PATH, {'class': 'blocklyAngleGauge'}, svg);
|
||||||
this.line_ = dom.createSvgElement(
|
this.line = dom.createSvgElement(
|
||||||
Svg.LINE, {
|
Svg.LINE, {
|
||||||
'x1': FieldAngle.HALF,
|
'x1': FieldAngle.HALF,
|
||||||
'y1': FieldAngle.HALF,
|
'y1': FieldAngle.HALF,
|
||||||
@@ -261,38 +253,30 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
// The angle picker is different from other fields in that it updates on
|
// The angle picker is different from other fields in that it updates on
|
||||||
// mousemove even if it's not in the middle of a drag. In future we may
|
// mousemove even if it's not in the middle of a drag. In future we may
|
||||||
// change this behaviour.
|
// change this behaviour.
|
||||||
this.clickWrapper_ =
|
this.boundEvents.push(
|
||||||
browserEvents.conditionalBind(svg, 'click', this, this.hide_);
|
browserEvents.conditionalBind(svg, 'click', this, this.hide));
|
||||||
// On touch devices, the picker's value is only updated with a drag. Add
|
// On touch devices, the picker's value is only updated with a drag. Add
|
||||||
// a click handler on the drag surface to update the value if the surface
|
// a click handler on the drag surface to update the value if the surface
|
||||||
// is clicked.
|
// is clicked.
|
||||||
this.clickSurfaceWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
circle, 'pointerdown', this, this.onMouseMove_, true);
|
circle, 'pointerdown', this, this.onMouseMove_, true));
|
||||||
this.moveSurfaceWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
circle, 'pointermove', this, this.onMouseMove_, true);
|
circle, 'pointermove', this, this.onMouseMove_, true));
|
||||||
this.editor_ = svg;
|
return svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disposes of events and DOM-references belonging to the angle editor. */
|
/** Disposes of events and DOM-references belonging to the angle editor. */
|
||||||
private dropdownDispose_() {
|
private dropdownDispose() {
|
||||||
if (this.clickWrapper_) {
|
for (const event of this.boundEvents) {
|
||||||
browserEvents.unbind(this.clickWrapper_);
|
browserEvents.unbind(event);
|
||||||
this.clickWrapper_ = null;
|
|
||||||
}
|
}
|
||||||
if (this.clickSurfaceWrapper_) {
|
this.boundEvents.length = 0;
|
||||||
browserEvents.unbind(this.clickSurfaceWrapper_);
|
this.gauge = null;
|
||||||
this.clickSurfaceWrapper_ = null;
|
this.line = null;
|
||||||
}
|
|
||||||
if (this.moveSurfaceWrapper_) {
|
|
||||||
browserEvents.unbind(this.moveSurfaceWrapper_);
|
|
||||||
this.moveSurfaceWrapper_ = null;
|
|
||||||
}
|
|
||||||
this.gauge_ = null;
|
|
||||||
this.line_ = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hide the editor. */
|
/** Hide the editor. */
|
||||||
private hide_() {
|
private hide() {
|
||||||
dropDownDiv.hideIfOwner(this);
|
dropDownDiv.hideIfOwner(this);
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
}
|
}
|
||||||
@@ -304,7 +288,7 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
*/
|
*/
|
||||||
protected onMouseMove_(e: PointerEvent) {
|
protected onMouseMove_(e: PointerEvent) {
|
||||||
// Calculate angle.
|
// Calculate angle.
|
||||||
const bBox = this.gauge_!.ownerSVGElement!.getBoundingClientRect();
|
const bBox = this.gauge!.ownerSVGElement!.getBoundingClientRect();
|
||||||
const dx = e.clientX - bBox.left - FieldAngle.HALF;
|
const dx = e.clientX - bBox.left - FieldAngle.HALF;
|
||||||
const dy = e.clientY - bBox.top - FieldAngle.HALF;
|
const dy = e.clientY - bBox.top - FieldAngle.HALF;
|
||||||
let angle = Math.atan(-dy / dx);
|
let angle = Math.atan(-dy / dx);
|
||||||
@@ -321,13 +305,13 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do offsetting.
|
// Do offsetting.
|
||||||
if (this.clockwise_) {
|
if (this.clockwise) {
|
||||||
angle = this.offset_ + 360 - angle;
|
angle = this.offset + 360 - angle;
|
||||||
} else {
|
} else {
|
||||||
angle = 360 - (this.offset_ - angle);
|
angle = 360 - (this.offset - angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.displayMouseOrKeyboardValue_(angle);
|
this.displayMouseOrKeyboardValue(angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -337,31 +321,31 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
*
|
*
|
||||||
* @param angle New angle.
|
* @param angle New angle.
|
||||||
*/
|
*/
|
||||||
private displayMouseOrKeyboardValue_(angle: number) {
|
private displayMouseOrKeyboardValue(angle: number) {
|
||||||
if (this.round_) {
|
if (this.round) {
|
||||||
angle = Math.round(angle / this.round_) * this.round_;
|
angle = Math.round(angle / this.round) * this.round;
|
||||||
}
|
}
|
||||||
angle = this.wrapValue_(angle);
|
angle = this.wrapValue(angle);
|
||||||
if (angle !== this.value_) {
|
if (angle !== this.value_) {
|
||||||
this.setEditorValue_(angle);
|
this.setEditorValue_(angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Redraw the graph with the current angle. */
|
/** Redraw the graph with the current angle. */
|
||||||
private updateGraph_() {
|
private updateGraph() {
|
||||||
if (!this.gauge_) {
|
if (!this.gauge || !this.line) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Always display the input (i.e. getText) even if it is invalid.
|
// Always display the input (i.e. getText) even if it is invalid.
|
||||||
let angleDegrees = Number(this.getText()) + this.offset_;
|
let angleDegrees = Number(this.getText()) + this.offset;
|
||||||
angleDegrees %= 360;
|
angleDegrees %= 360;
|
||||||
let angleRadians = math.toRadians(angleDegrees);
|
let angleRadians = math.toRadians(angleDegrees);
|
||||||
const path = ['M ', FieldAngle.HALF, ',', FieldAngle.HALF];
|
const path = ['M ', FieldAngle.HALF, ',', FieldAngle.HALF];
|
||||||
let x2 = FieldAngle.HALF;
|
let x2 = FieldAngle.HALF;
|
||||||
let y2 = FieldAngle.HALF;
|
let y2 = FieldAngle.HALF;
|
||||||
if (!isNaN(angleRadians)) {
|
if (!isNaN(angleRadians)) {
|
||||||
const clockwiseFlag = Number(this.clockwise_);
|
const clockwiseFlag = Number(this.clockwise);
|
||||||
const angle1 = math.toRadians(this.offset_);
|
const angle1 = math.toRadians(this.offset);
|
||||||
const x1 = Math.cos(angle1) * FieldAngle.RADIUS;
|
const x1 = Math.cos(angle1) * FieldAngle.RADIUS;
|
||||||
const y1 = Math.sin(angle1) * -FieldAngle.RADIUS;
|
const y1 = Math.sin(angle1) * -FieldAngle.RADIUS;
|
||||||
if (clockwiseFlag) {
|
if (clockwiseFlag) {
|
||||||
@@ -379,9 +363,9 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
' l ', x1, ',', y1, ' A ', FieldAngle.RADIUS, ',', FieldAngle.RADIUS,
|
' l ', x1, ',', y1, ' A ', FieldAngle.RADIUS, ',', FieldAngle.RADIUS,
|
||||||
' 0 ', largeFlag, ' ', clockwiseFlag, ' ', x2, ',', y2, ' z');
|
' 0 ', largeFlag, ' ', clockwiseFlag, ' ', x2, ',', y2, ' z');
|
||||||
}
|
}
|
||||||
this.gauge_.setAttribute('d', path.join(''));
|
this.gauge.setAttribute('d', path.join(''));
|
||||||
this.line_?.setAttribute('x2', `${x2}`);
|
this.line.setAttribute('x2', `${x2}`);
|
||||||
this.line_?.setAttribute('y2', `${y2}`);
|
this.line.setAttribute('y2', `${y2}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -396,23 +380,28 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
throw new UnattachedFieldError();
|
throw new UnattachedFieldError();
|
||||||
}
|
}
|
||||||
|
|
||||||
let multiplier;
|
let multiplier = 0;
|
||||||
if (e.keyCode === KeyCodes.LEFT) {
|
switch (e.key) {
|
||||||
// decrement (increment in RTL)
|
case 'ArrowLeft':
|
||||||
multiplier = block.RTL ? 1 : -1;
|
// decrement (increment in RTL)
|
||||||
} else if (e.keyCode === KeyCodes.RIGHT) {
|
multiplier = block.RTL ? 1 : -1;
|
||||||
// increment (decrement in RTL)
|
break;
|
||||||
multiplier = block.RTL ? -1 : 1;
|
case 'ArrowRight':
|
||||||
} else if (e.keyCode === KeyCodes.DOWN) {
|
// increment (decrement in RTL)
|
||||||
// decrement
|
multiplier = block.RTL ? -1 : 1;
|
||||||
multiplier = -1;
|
break;
|
||||||
} else if (e.keyCode === KeyCodes.UP) {
|
case 'ArrowDown':
|
||||||
// increment
|
// decrement
|
||||||
multiplier = 1;
|
multiplier = -1;
|
||||||
|
break;
|
||||||
|
case 'ArrowUp':
|
||||||
|
// increment
|
||||||
|
multiplier = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (multiplier) {
|
if (multiplier) {
|
||||||
const value = this.getValue() as number;
|
const value = this.getValue() as number;
|
||||||
this.displayMouseOrKeyboardValue_(value + multiplier * this.round_);
|
this.displayMouseOrKeyboardValue(value + multiplier * this.round);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
@@ -421,15 +410,15 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the input value is a valid angle.
|
* Ensure that the input value is a valid angle.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid angle, or null if invalid.
|
* @returns A valid angle, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: any): number|null {
|
protected override doClassValidation_(newValue?: any): number|null {
|
||||||
const value = Number(opt_newValue);
|
const value = Number(newValue);
|
||||||
if (isNaN(value) || !isFinite(value)) {
|
if (isNaN(value) || !isFinite(value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.wrapValue_(value);
|
return this.wrapValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -438,12 +427,12 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
* @param value The value to wrap.
|
* @param value The value to wrap.
|
||||||
* @returns The wrapped value.
|
* @returns The wrapped value.
|
||||||
*/
|
*/
|
||||||
private wrapValue_(value: number): number {
|
private wrapValue(value: number): number {
|
||||||
value %= 360;
|
value %= 360;
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
value += 360;
|
value += 360;
|
||||||
}
|
}
|
||||||
if (value > this.wrap_) {
|
if (value > this.wrap) {
|
||||||
value -= 360;
|
value -= 360;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@@ -464,13 +453,20 @@ export class FieldAngle extends FieldInput<number> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** CSS for angle field. See css.js for use. */
|
fieldRegistry.register('field_angle', FieldAngle);
|
||||||
|
|
||||||
|
FieldAngle.prototype.DEFAULT_VALUE = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS for angle field.
|
||||||
|
*/
|
||||||
Css.register(`
|
Css.register(`
|
||||||
.blocklyAngleCircle {
|
.blocklyAngleCircle {
|
||||||
stroke: #444;
|
stroke: #444;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
fill: #ddd;
|
fill: #ddd;
|
||||||
fill-opacity: .8;
|
fill-opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blocklyAngleMarks {
|
.blocklyAngleMarks {
|
||||||
@@ -480,7 +476,7 @@ Css.register(`
|
|||||||
|
|
||||||
.blocklyAngleGauge {
|
.blocklyAngleGauge {
|
||||||
fill: #f88;
|
fill: #f88;
|
||||||
fill-opacity: .8;
|
fill-opacity: 0.8;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,10 +488,6 @@ Css.register(`
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
fieldRegistry.register('field_angle', FieldAngle);
|
|
||||||
|
|
||||||
FieldAngle.prototype.DEFAULT_VALUE = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The two main modes of the angle field.
|
* The two main modes of the angle field.
|
||||||
* Compass specifies:
|
* Compass specifies:
|
||||||
|
|||||||
@@ -49,22 +49,22 @@ export class FieldCheckbox extends Field<CheckboxBool> {
|
|||||||
override value_: boolean|null = this.value_;
|
override value_: boolean|null = this.value_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should either be 'TRUE',
|
* @param value The initial value of the field. Should either be 'TRUE',
|
||||||
* 'FALSE' or a boolean. Defaults to 'FALSE'. Also accepts
|
* 'FALSE' or a boolean. Defaults to 'FALSE'. Also accepts
|
||||||
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
||||||
* that want to handle configuration and setting the field value after
|
* that want to handle configuration and setting the field value after
|
||||||
* their own constructors have run).
|
* their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a value ('TRUE' or 'FALSE') & returns a
|
* field's value. Takes in a value ('TRUE' or 'FALSE') & returns a
|
||||||
* validated value ('TRUE' or 'FALSE'), or null to abort the change.
|
* validated value ('TRUE' or 'FALSE'), or null to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/checkbox#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/checkbox#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: CheckboxBool|Sentinel, opt_validator?: FieldCheckboxValidator,
|
value?: CheckboxBool|Sentinel, validator?: FieldCheckboxValidator,
|
||||||
opt_config?: FieldCheckboxConfig) {
|
config?: FieldCheckboxConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,13 +73,13 @@ export class FieldCheckbox extends Field<CheckboxBool> {
|
|||||||
*/
|
*/
|
||||||
this.checkChar_ = FieldCheckbox.CHECK_CHAR;
|
this.checkChar_ = FieldCheckbox.CHECK_CHAR;
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,15 +150,15 @@ export class FieldCheckbox extends Field<CheckboxBool> {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the input value is valid ('TRUE' or 'FALSE').
|
* Ensure that the input value is valid ('TRUE' or 'FALSE').
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid value ('TRUE' or 'FALSE), or null if invalid.
|
* @returns A valid value ('TRUE' or 'FALSE), or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
protected override doClassValidation_(newValue?: AnyDuringMigration):
|
||||||
BoolString|null {
|
BoolString|null {
|
||||||
if (opt_newValue === true || opt_newValue === 'TRUE') {
|
if (newValue === true || newValue === 'TRUE') {
|
||||||
return 'TRUE';
|
return 'TRUE';
|
||||||
}
|
}
|
||||||
if (opt_newValue === false || opt_newValue === 'FALSE') {
|
if (newValue === false || newValue === 'FALSE') {
|
||||||
return 'FALSE';
|
return 'FALSE';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import * as fieldRegistry from './field_registry.js';
|
|||||||
import * as aria from './utils/aria.js';
|
import * as aria from './utils/aria.js';
|
||||||
import * as colour from './utils/colour.js';
|
import * as colour from './utils/colour.js';
|
||||||
import * as idGenerator from './utils/idgenerator.js';
|
import * as idGenerator from './utils/idgenerator.js';
|
||||||
import {KeyCodes} from './utils/keycodes.js';
|
|
||||||
import type {Sentinel} from './utils/sentinel.js';
|
import type {Sentinel} from './utils/sentinel.js';
|
||||||
import {Size} from './utils/size.js';
|
import {Size} from './utils/size.js';
|
||||||
|
|
||||||
@@ -76,25 +75,17 @@ export class FieldColour extends Field<string> {
|
|||||||
static COLUMNS = 7;
|
static COLUMNS = 7;
|
||||||
|
|
||||||
/** The field's colour picker element. */
|
/** The field's colour picker element. */
|
||||||
private picker_: HTMLElement|null = null;
|
private picker: HTMLElement|null = null;
|
||||||
|
|
||||||
/** Index of the currently highlighted element. */
|
/** Index of the currently highlighted element. */
|
||||||
private highlightedIndex_: number|null = null;
|
private highlightedIndex: number|null = null;
|
||||||
|
|
||||||
/** Mouse click event data. */
|
/**
|
||||||
private onClickWrapper_: browserEvents.Data|null = null;
|
* Array holding info needed to unbind events.
|
||||||
|
* Used for disposing.
|
||||||
/** Mouse move event data. */
|
* Ex: [[node, name, func], [node, name, func]].
|
||||||
private onMouseMoveWrapper_: browserEvents.Data|null = null;
|
*/
|
||||||
|
private boundEvents: browserEvents.Data[] = [];
|
||||||
/** Mouse enter event data. */
|
|
||||||
private onMouseEnterWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** Mouse leave event data. */
|
|
||||||
private onMouseLeaveWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** Key down event data. */
|
|
||||||
private onKeyDownWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializable fields are saved by the serializer, non-serializable fields
|
* Serializable fields are saved by the serializer, non-serializable fields
|
||||||
@@ -113,46 +104,46 @@ export class FieldColour extends Field<string> {
|
|||||||
protected override isDirty_ = false;
|
protected override isDirty_ = false;
|
||||||
|
|
||||||
/** Array of colours used by this field. If null, use the global list. */
|
/** Array of colours used by this field. If null, use the global list. */
|
||||||
private colours_: string[]|null = null;
|
private colours: string[]|null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of colour tooltips used by this field. If null, use the global
|
* Array of colour tooltips used by this field. If null, use the global
|
||||||
* list.
|
* list.
|
||||||
*/
|
*/
|
||||||
private titles_: string[]|null = null;
|
private titles: string[]|null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of colour columns used by this field. If 0, use the global
|
* Number of colour columns used by this field. If 0, use the global
|
||||||
* setting. By default use the global constants for columns.
|
* setting. By default use the global constants for columns.
|
||||||
*/
|
*/
|
||||||
private columns_ = 0;
|
private columns = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should be in '#rrggbb'
|
* @param value The initial value of the field. Should be in '#rrggbb'
|
||||||
* format. Defaults to the first value in the default colour array. Also
|
* format. Defaults to the first value in the default colour array. Also
|
||||||
* accepts Field.SKIP_SETUP if you wish to skip setup (only used by
|
* accepts Field.SKIP_SETUP if you wish to skip setup (only used by
|
||||||
* subclasses that want to handle configuration and setting the field
|
* subclasses that want to handle configuration and setting the field
|
||||||
* value after their own constructors have run).
|
* value after their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a colour string & returns a validated colour
|
* field's value. Takes in a colour string & returns a validated colour
|
||||||
* string ('#rrggbb' format), or null to abort the change.Blockly.
|
* string ('#rrggbb' format), or null to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|Sentinel, opt_validator?: FieldColourValidator,
|
value?: string|Sentinel, validator?: FieldColourValidator,
|
||||||
opt_config?: FieldColourConfig) {
|
config?: FieldColourConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,9 +154,9 @@ export class FieldColour extends Field<string> {
|
|||||||
*/
|
*/
|
||||||
protected override configure_(config: FieldColourConfig) {
|
protected override configure_(config: FieldColourConfig) {
|
||||||
super.configure_(config);
|
super.configure_(config);
|
||||||
if (config.colourOptions) this.colours_ = config.colourOptions;
|
if (config.colourOptions) this.colours = config.colourOptions;
|
||||||
if (config.colourTitles) this.titles_ = config.colourTitles;
|
if (config.colourTitles) this.titles = config.colourTitles;
|
||||||
if (config.columns) this.columns_ = config.columns;
|
if (config.columns) this.columns = config.columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,6 +176,11 @@ export class FieldColour extends Field<string> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates text field to match the colour/style of the block.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
override applyColour() {
|
override applyColour() {
|
||||||
if (!this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) {
|
if (!this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) {
|
||||||
if (this.borderRect_) {
|
if (this.borderRect_) {
|
||||||
@@ -200,14 +196,14 @@ export class FieldColour extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the input value is a valid colour.
|
* Ensure that the input value is a valid colour.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid colour, or null if invalid.
|
* @returns A valid colour, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: any): string|null {
|
protected override doClassValidation_(newValue?: any): string|null {
|
||||||
if (typeof opt_newValue !== 'string') {
|
if (typeof newValue !== 'string') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return colour.parse(opt_newValue);
|
return colour.parse(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -247,14 +243,14 @@ export class FieldColour extends Field<string> {
|
|||||||
*
|
*
|
||||||
* @param colours Array of colours for this block, or null to use default
|
* @param colours Array of colours for this block, or null to use default
|
||||||
* (FieldColour.COLOURS).
|
* (FieldColour.COLOURS).
|
||||||
* @param opt_titles Optional array of colour tooltips, or null to use default
|
* @param titles Optional array of colour tooltips, or null to use default
|
||||||
* (FieldColour.TITLES).
|
* (FieldColour.TITLES).
|
||||||
* @returns Returns itself (for method chaining).
|
* @returns Returns itself (for method chaining).
|
||||||
*/
|
*/
|
||||||
setColours(colours: string[], opt_titles?: string[]): FieldColour {
|
setColours(colours: string[], titles?: string[]): FieldColour {
|
||||||
this.colours_ = colours;
|
this.colours = colours;
|
||||||
if (opt_titles) {
|
if (titles) {
|
||||||
this.titles_ = opt_titles;
|
this.titles = titles;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -267,19 +263,19 @@ export class FieldColour extends Field<string> {
|
|||||||
* @returns Returns itself (for method chaining).
|
* @returns Returns itself (for method chaining).
|
||||||
*/
|
*/
|
||||||
setColumns(columns: number): FieldColour {
|
setColumns(columns: number): FieldColour {
|
||||||
this.columns_ = columns;
|
this.columns = columns;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create and show the colour field's editor. */
|
/** Create and show the colour field's editor. */
|
||||||
protected override showEditor_() {
|
protected override showEditor_() {
|
||||||
this.dropdownCreate_();
|
this.dropdownCreate();
|
||||||
dropDownDiv.getContentDiv().appendChild(this.picker_!);
|
dropDownDiv.getContentDiv().appendChild(this.picker!);
|
||||||
|
|
||||||
dropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
|
dropDownDiv.showPositionedByField(this, this.dropdownDispose.bind(this));
|
||||||
|
|
||||||
// Focus so we can start receiving keyboard events.
|
// Focus so we can start receiving keyboard events.
|
||||||
this.picker_!.focus({preventScroll: true});
|
this.picker!.focus({preventScroll: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,7 +283,7 @@ export class FieldColour extends Field<string> {
|
|||||||
*
|
*
|
||||||
* @param e Mouse event.
|
* @param e Mouse event.
|
||||||
*/
|
*/
|
||||||
private onClick_(e: PointerEvent) {
|
private onClick(e: PointerEvent) {
|
||||||
const cell = e.target as Element;
|
const cell = e.target as Element;
|
||||||
const colour = cell && cell.getAttribute('data-colour');
|
const colour = cell && cell.getAttribute('data-colour');
|
||||||
if (colour !== null) {
|
if (colour !== null) {
|
||||||
@@ -302,31 +298,35 @@ export class FieldColour extends Field<string> {
|
|||||||
*
|
*
|
||||||
* @param e Keyboard event.
|
* @param e Keyboard event.
|
||||||
*/
|
*/
|
||||||
private onKeyDown_(e: KeyboardEvent) {
|
private onKeyDown(e: KeyboardEvent) {
|
||||||
let handled = false;
|
let handled = true;
|
||||||
if (e.keyCode === KeyCodes.UP) {
|
let highlighted: HTMLElement|null;
|
||||||
this.moveHighlightBy_(0, -1);
|
switch (e.key) {
|
||||||
handled = true;
|
case 'ArrowUp':
|
||||||
} else if (e.keyCode === KeyCodes.DOWN) {
|
this.moveHighlightBy(0, -1);
|
||||||
this.moveHighlightBy_(0, 1);
|
break;
|
||||||
handled = true;
|
case 'ArrowDown':
|
||||||
} else if (e.keyCode === KeyCodes.LEFT) {
|
this.moveHighlightBy(0, 1);
|
||||||
this.moveHighlightBy_(-1, 0);
|
break;
|
||||||
handled = true;
|
case 'ArrowLeft':
|
||||||
} else if (e.keyCode === KeyCodes.RIGHT) {
|
this.moveHighlightBy(-1, 0);
|
||||||
this.moveHighlightBy_(1, 0);
|
break;
|
||||||
handled = true;
|
case 'ArrowRight':
|
||||||
} else if (e.keyCode === KeyCodes.ENTER) {
|
this.moveHighlightBy(1, 0);
|
||||||
// Select the highlighted colour.
|
break;
|
||||||
const highlighted = this.getHighlighted_();
|
case 'Enter':
|
||||||
if (highlighted) {
|
// Select the highlighted colour.
|
||||||
const colour = highlighted && highlighted.getAttribute('data-colour');
|
highlighted = this.getHighlighted();
|
||||||
if (colour !== null) {
|
if (highlighted) {
|
||||||
this.setValue(colour);
|
const colour = highlighted.getAttribute('data-colour');
|
||||||
|
if (colour !== null) {
|
||||||
|
this.setValue(colour);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
dropDownDiv.hideWithoutAnimation();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
break;
|
||||||
handled = true;
|
default:
|
||||||
|
handled = false;
|
||||||
}
|
}
|
||||||
if (handled) {
|
if (handled) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -336,22 +336,22 @@ export class FieldColour extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Move the currently highlighted position by dx and dy.
|
* Move the currently highlighted position by dx and dy.
|
||||||
*
|
*
|
||||||
* @param dx Change of x
|
* @param dx Change of x.
|
||||||
* @param dy Change of y
|
* @param dy Change of y.
|
||||||
*/
|
*/
|
||||||
private moveHighlightBy_(dx: number, dy: number) {
|
private moveHighlightBy(dx: number, dy: number) {
|
||||||
if (!this.highlightedIndex_) {
|
if (!this.highlightedIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colours = this.colours_ || FieldColour.COLOURS;
|
const colours = this.colours || FieldColour.COLOURS;
|
||||||
const columns = this.columns_ || FieldColour.COLUMNS;
|
const columns = this.columns || FieldColour.COLUMNS;
|
||||||
|
|
||||||
// Get the current x and y coordinates
|
// Get the current x and y coordinates.
|
||||||
let x = this.highlightedIndex_ % columns;
|
let x = this.highlightedIndex % columns;
|
||||||
let y = Math.floor(this.highlightedIndex_ / columns);
|
let y = Math.floor(this.highlightedIndex / columns);
|
||||||
|
|
||||||
// Add the offset
|
// Add the offset.
|
||||||
x += dx;
|
x += dx;
|
||||||
y += dy;
|
y += dy;
|
||||||
|
|
||||||
@@ -386,9 +386,9 @@ export class FieldColour extends Field<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move the highlight to the new coordinates.
|
// Move the highlight to the new coordinates.
|
||||||
const cell = this.picker_!.childNodes[y].childNodes[x] as Element;
|
const cell = this.picker!.childNodes[y].childNodes[x] as Element;
|
||||||
const index = y * columns + x;
|
const index = y * columns + x;
|
||||||
this.setHighlightedCell_(cell, index);
|
this.setHighlightedCell(cell, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -396,26 +396,26 @@ export class FieldColour extends Field<string> {
|
|||||||
*
|
*
|
||||||
* @param e Mouse event.
|
* @param e Mouse event.
|
||||||
*/
|
*/
|
||||||
private onMouseMove_(e: PointerEvent) {
|
private onMouseMove(e: PointerEvent) {
|
||||||
const cell = e.target as Element;
|
const cell = e.target as Element;
|
||||||
const index = cell && Number(cell.getAttribute('data-index'));
|
const index = cell && Number(cell.getAttribute('data-index'));
|
||||||
if (index !== null && index !== this.highlightedIndex_) {
|
if (index !== null && index !== this.highlightedIndex) {
|
||||||
this.setHighlightedCell_(cell, index);
|
this.setHighlightedCell(cell, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle a mouse enter event. Focus the picker. */
|
/** Handle a mouse enter event. Focus the picker. */
|
||||||
private onMouseEnter_() {
|
private onMouseEnter() {
|
||||||
this.picker_?.focus({preventScroll: true});
|
this.picker?.focus({preventScroll: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a mouse leave event. Blur the picker and unhighlight
|
* Handle a mouse leave event. Blur the picker and unhighlight
|
||||||
* the currently highlighted colour.
|
* the currently highlighted colour.
|
||||||
*/
|
*/
|
||||||
private onMouseLeave_() {
|
private onMouseLeave() {
|
||||||
this.picker_?.blur();
|
this.picker?.blur();
|
||||||
const highlighted = this.getHighlighted_();
|
const highlighted = this.getHighlighted();
|
||||||
if (highlighted) {
|
if (highlighted) {
|
||||||
dom.removeClass(highlighted, 'blocklyColourHighlighted');
|
dom.removeClass(highlighted, 'blocklyColourHighlighted');
|
||||||
}
|
}
|
||||||
@@ -426,54 +426,53 @@ export class FieldColour extends Field<string> {
|
|||||||
*
|
*
|
||||||
* @returns Highlighted item (null if none).
|
* @returns Highlighted item (null if none).
|
||||||
*/
|
*/
|
||||||
private getHighlighted_(): HTMLElement|null {
|
private getHighlighted(): HTMLElement|null {
|
||||||
if (!this.highlightedIndex_) {
|
if (!this.highlightedIndex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = this.columns_ || FieldColour.COLUMNS;
|
const columns = this.columns || FieldColour.COLUMNS;
|
||||||
const x = this.highlightedIndex_ % columns;
|
const x = this.highlightedIndex % columns;
|
||||||
const y = Math.floor(this.highlightedIndex_ / columns);
|
const y = Math.floor(this.highlightedIndex / columns);
|
||||||
const row = this.picker_!.childNodes[y];
|
const row = this.picker!.childNodes[y];
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const col = row.childNodes[x] as HTMLElement;
|
return row.childNodes[x] as HTMLElement;
|
||||||
return col;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the currently highlighted cell.
|
* Update the currently highlighted cell.
|
||||||
*
|
*
|
||||||
* @param cell the new cell to highlight
|
* @param cell The new cell to highlight.
|
||||||
* @param index the index of the new cell
|
* @param index The index of the new cell.
|
||||||
*/
|
*/
|
||||||
private setHighlightedCell_(cell: Element, index: number) {
|
private setHighlightedCell(cell: Element, index: number) {
|
||||||
// Unhighlight the current item.
|
// Unhighlight the current item.
|
||||||
const highlighted = this.getHighlighted_();
|
const highlighted = this.getHighlighted();
|
||||||
if (highlighted) {
|
if (highlighted) {
|
||||||
dom.removeClass(highlighted, 'blocklyColourHighlighted');
|
dom.removeClass(highlighted, 'blocklyColourHighlighted');
|
||||||
}
|
}
|
||||||
// Highlight new item.
|
// Highlight new item.
|
||||||
dom.addClass(cell, 'blocklyColourHighlighted');
|
dom.addClass(cell, 'blocklyColourHighlighted');
|
||||||
// Set new highlighted index.
|
// Set new highlighted index.
|
||||||
this.highlightedIndex_ = index;
|
this.highlightedIndex = index;
|
||||||
|
|
||||||
// Update accessibility roles.
|
// Update accessibility roles.
|
||||||
const cellId = cell.getAttribute('id');
|
const cellId = cell.getAttribute('id');
|
||||||
if (cellId && this.picker_) {
|
if (cellId && this.picker) {
|
||||||
aria.setState(this.picker_, aria.State.ACTIVEDESCENDANT, cellId);
|
aria.setState(this.picker, aria.State.ACTIVEDESCENDANT, cellId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a colour picker dropdown editor. */
|
/** Create a colour picker dropdown editor. */
|
||||||
private dropdownCreate_() {
|
private dropdownCreate() {
|
||||||
const columns = this.columns_ || FieldColour.COLUMNS;
|
const columns = this.columns || FieldColour.COLUMNS;
|
||||||
const colours = this.colours_ || FieldColour.COLOURS;
|
const colours = this.colours || FieldColour.COLOURS;
|
||||||
const titles = this.titles_ || FieldColour.TITLES;
|
const titles = this.titles || FieldColour.TITLES;
|
||||||
const selectedColour = this.getValue();
|
const selectedColour = this.getValue();
|
||||||
// Create the palette.
|
// Create the palette.
|
||||||
const table = (document.createElement('table'));
|
const table = document.createElement('table');
|
||||||
table.className = 'blocklyColourTable';
|
table.className = 'blocklyColourTable';
|
||||||
table.tabIndex = 0;
|
table.tabIndex = 0;
|
||||||
table.dir = 'ltr';
|
table.dir = 'ltr';
|
||||||
@@ -495,56 +494,40 @@ export class FieldColour extends Field<string> {
|
|||||||
cell.setAttribute('data-colour', colours[i]);
|
cell.setAttribute('data-colour', colours[i]);
|
||||||
cell.title = titles[i] || colours[i];
|
cell.title = titles[i] || colours[i];
|
||||||
cell.id = idGenerator.getNextUniqueId();
|
cell.id = idGenerator.getNextUniqueId();
|
||||||
cell.setAttribute('data-index', String(i));
|
cell.setAttribute('data-index', `${i}`);
|
||||||
aria.setRole(cell, aria.Role.GRIDCELL);
|
aria.setRole(cell, aria.Role.GRIDCELL);
|
||||||
aria.setState(cell, aria.State.LABEL, colours[i]);
|
aria.setState(cell, aria.State.LABEL, colours[i]);
|
||||||
aria.setState(cell, aria.State.SELECTED, colours[i] === selectedColour);
|
aria.setState(cell, aria.State.SELECTED, colours[i] === selectedColour);
|
||||||
cell.style.backgroundColor = colours[i];
|
cell.style.backgroundColor = colours[i];
|
||||||
if (colours[i] === selectedColour) {
|
if (colours[i] === selectedColour) {
|
||||||
cell.className = 'blocklyColourSelected';
|
cell.className = 'blocklyColourSelected';
|
||||||
this.highlightedIndex_ = i;
|
this.highlightedIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure event handler on the table to listen for any event in a cell.
|
// Configure event handler on the table to listen for any event in a cell.
|
||||||
this.onClickWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
table, 'pointerdown', this, this.onClick_, true);
|
table, 'pointerdown', this, this.onClick, true));
|
||||||
this.onMouseMoveWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
table, 'pointermove', this, this.onMouseMove_, true);
|
table, 'pointermove', this, this.onMouseMove, true));
|
||||||
this.onMouseEnterWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
table, 'pointerenter', this, this.onMouseEnter_, true);
|
table, 'pointerenter', this, this.onMouseEnter, true));
|
||||||
this.onMouseLeaveWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
table, 'pointerleave', this, this.onMouseLeave_, true);
|
table, 'pointerleave', this, this.onMouseLeave, true));
|
||||||
this.onKeyDownWrapper_ =
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
browserEvents.conditionalBind(table, 'keydown', this, this.onKeyDown_);
|
table, 'keydown', this, this.onKeyDown, false));
|
||||||
|
|
||||||
this.picker_ = table;
|
this.picker = table;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disposes of events and DOM-references belonging to the colour editor. */
|
/** Disposes of events and DOM-references belonging to the colour editor. */
|
||||||
private dropdownDispose_() {
|
private dropdownDispose() {
|
||||||
if (this.onClickWrapper_) {
|
for (const event of this.boundEvents) {
|
||||||
browserEvents.unbind(this.onClickWrapper_);
|
browserEvents.unbind(event);
|
||||||
this.onClickWrapper_ = null;
|
|
||||||
}
|
}
|
||||||
if (this.onMouseMoveWrapper_) {
|
this.boundEvents.length = 0;
|
||||||
browserEvents.unbind(this.onMouseMoveWrapper_);
|
this.picker = null;
|
||||||
this.onMouseMoveWrapper_ = null;
|
this.highlightedIndex = null;
|
||||||
}
|
|
||||||
if (this.onMouseEnterWrapper_) {
|
|
||||||
browserEvents.unbind(this.onMouseEnterWrapper_);
|
|
||||||
this.onMouseEnterWrapper_ = null;
|
|
||||||
}
|
|
||||||
if (this.onMouseLeaveWrapper_) {
|
|
||||||
browserEvents.unbind(this.onMouseLeaveWrapper_);
|
|
||||||
this.onMouseLeaveWrapper_ = null;
|
|
||||||
}
|
|
||||||
if (this.onKeyDownWrapper_) {
|
|
||||||
browserEvents.unbind(this.onKeyDownWrapper_);
|
|
||||||
this.onKeyDownWrapper_ = null;
|
|
||||||
}
|
|
||||||
this.picker_ = null;
|
|
||||||
this.highlightedIndex_ = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -565,7 +548,12 @@ export class FieldColour extends Field<string> {
|
|||||||
/** The default value for this field. */
|
/** The default value for this field. */
|
||||||
FieldColour.prototype.DEFAULT_VALUE = FieldColour.COLOURS[0];
|
FieldColour.prototype.DEFAULT_VALUE = FieldColour.COLOURS[0];
|
||||||
|
|
||||||
/** CSS for colour picker. See css.js for use. */
|
fieldRegistry.register('field_colour', FieldColour);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS for colour picker.
|
||||||
|
*/
|
||||||
Css.register(`
|
Css.register(`
|
||||||
.blocklyColourTable {
|
.blocklyColourTable {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
@@ -575,7 +563,7 @@ Css.register(`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.blocklyColourTable>tr>td {
|
.blocklyColourTable>tr>td {
|
||||||
border: .5px solid #888;
|
border: 0.5px solid #888;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -586,7 +574,7 @@ Css.register(`
|
|||||||
|
|
||||||
.blocklyColourTable>tr>td.blocklyColourHighlighted {
|
.blocklyColourTable>tr>td.blocklyColourHighlighted {
|
||||||
border-color: #eee;
|
border-color: #eee;
|
||||||
box-shadow: 2px 2px 7px 2px rgba(0,0,0,.3);
|
box-shadow: 2px 2px 7px 2px rgba(0, 0, 0, 0.3);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,8 +585,6 @@ Css.register(`
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
fieldRegistry.register('field_colour', FieldColour);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config options for the colour field.
|
* Config options for the colour field.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -97,11 +97,11 @@ export class FieldDropdown extends Field<string> {
|
|||||||
* if you wish to skip setup (only used by subclasses that want to handle
|
* if you wish to skip setup (only used by subclasses that want to handle
|
||||||
* configuration and setting the field value after their own constructors
|
* configuration and setting the field value after their own constructors
|
||||||
* have run).
|
* have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a language-neutral dropdown option & returns a
|
* field's value. Takes in a language-neutral dropdown option & returns a
|
||||||
* validated language-neutral dropdown option, or null to abort the
|
* validated language-neutral dropdown option, or null to abort the
|
||||||
* change.
|
* change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
@@ -109,14 +109,14 @@ export class FieldDropdown extends Field<string> {
|
|||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
menuGenerator: MenuGenerator,
|
menuGenerator: MenuGenerator,
|
||||||
opt_validator?: FieldDropdownValidator,
|
validator?: FieldDropdownValidator,
|
||||||
opt_config?: FieldDropdownConfig,
|
config?: FieldDropdownConfig,
|
||||||
);
|
);
|
||||||
constructor(menuGenerator: Sentinel);
|
constructor(menuGenerator: Sentinel);
|
||||||
constructor(
|
constructor(
|
||||||
menuGenerator: MenuGenerator|Sentinel,
|
menuGenerator: MenuGenerator|Sentinel,
|
||||||
opt_validator?: FieldDropdownValidator,
|
validator?: FieldDropdownValidator,
|
||||||
opt_config?: FieldDropdownConfig,
|
config?: FieldDropdownConfig,
|
||||||
) {
|
) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
@@ -139,12 +139,12 @@ export class FieldDropdown extends Field<string> {
|
|||||||
*/
|
*/
|
||||||
this.selectedOption_ = this.getOptions(false)[0];
|
this.selectedOption_ = this.getOptions(false)[0];
|
||||||
|
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(this.selectedOption_[1]);
|
this.setValue(this.selectedOption_[1]);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,17 +244,17 @@ export class FieldDropdown extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Create a dropdown menu under the text.
|
* Create a dropdown menu under the text.
|
||||||
*
|
*
|
||||||
* @param opt_e Optional mouse event that triggered the field to open, or
|
* @param e Optional mouse event that triggered the field to open, or
|
||||||
* undefined if triggered programmatically.
|
* undefined if triggered programmatically.
|
||||||
*/
|
*/
|
||||||
protected override showEditor_(opt_e?: MouseEvent) {
|
protected override showEditor_(e?: MouseEvent) {
|
||||||
const block = this.getSourceBlock();
|
const block = this.getSourceBlock();
|
||||||
if (!block) {
|
if (!block) {
|
||||||
throw new UnattachedFieldError();
|
throw new UnattachedFieldError();
|
||||||
}
|
}
|
||||||
this.dropdownCreate_();
|
this.dropdownCreate_();
|
||||||
if (opt_e && typeof opt_e.clientX === 'number') {
|
if (e && typeof e.clientX === 'number') {
|
||||||
this.menu_!.openingCoords = new Coordinate(opt_e.clientX, opt_e.clientY);
|
this.menu_!.openingCoords = new Coordinate(e.clientX, e.clientY);
|
||||||
} else {
|
} else {
|
||||||
this.menu_!.openingCoords = null;
|
this.menu_!.openingCoords = null;
|
||||||
}
|
}
|
||||||
@@ -368,20 +368,20 @@ export class FieldDropdown extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Return a list of the options for this dropdown.
|
* Return a list of the options for this dropdown.
|
||||||
*
|
*
|
||||||
* @param opt_useCache For dynamic options, whether or not to use the cached
|
* @param useCache For dynamic options, whether or not to use the cached
|
||||||
* options or to re-generate them.
|
* options or to re-generate them.
|
||||||
* @returns A non-empty array of option tuples:
|
* @returns A non-empty array of option tuples:
|
||||||
* (human-readable text or image, language-neutral name).
|
* (human-readable text or image, language-neutral name).
|
||||||
* @throws {TypeError} If generated options are incorrectly structured.
|
* @throws {TypeError} If generated options are incorrectly structured.
|
||||||
*/
|
*/
|
||||||
getOptions(opt_useCache?: boolean): MenuOption[] {
|
getOptions(useCache?: boolean): MenuOption[] {
|
||||||
if (!this.menuGenerator_) {
|
if (!this.menuGenerator_) {
|
||||||
// A subclass improperly skipped setup without defining the menu
|
// A subclass improperly skipped setup without defining the menu
|
||||||
// generator.
|
// generator.
|
||||||
throw TypeError('A menu generator was never defined.');
|
throw TypeError('A menu generator was never defined.');
|
||||||
}
|
}
|
||||||
if (Array.isArray(this.menuGenerator_)) return this.menuGenerator_;
|
if (Array.isArray(this.menuGenerator_)) return this.menuGenerator_;
|
||||||
if (opt_useCache && this.generatedOptions_) return this.generatedOptions_;
|
if (useCache && this.generatedOptions_) return this.generatedOptions_;
|
||||||
|
|
||||||
this.generatedOptions_ = this.menuGenerator_();
|
this.generatedOptions_ = this.menuGenerator_();
|
||||||
validateOptions(this.generatedOptions_);
|
validateOptions(this.generatedOptions_);
|
||||||
@@ -391,23 +391,23 @@ export class FieldDropdown extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the input value is a valid language-neutral option.
|
* Ensure that the input value is a valid language-neutral option.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid language-neutral option, or null if invalid.
|
* @returns A valid language-neutral option, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: string): string|null {
|
protected override doClassValidation_(newValue?: string): string|null {
|
||||||
const options = this.getOptions(true);
|
const options = this.getOptions(true);
|
||||||
const isValueValid = options.some((option) => option[1] === opt_newValue);
|
const isValueValid = options.some((option) => option[1] === newValue);
|
||||||
|
|
||||||
if (!isValueValid) {
|
if (!isValueValid) {
|
||||||
if (this.sourceBlock_) {
|
if (this.sourceBlock_) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Cannot set the dropdown\'s value to an unavailable option.' +
|
'Cannot set the dropdown\'s value to an unavailable option.' +
|
||||||
' Block type: ' + this.sourceBlock_.type +
|
' Block type: ' + this.sourceBlock_.type +
|
||||||
', Field name: ' + this.name + ', Value: ' + opt_newValue);
|
', Field name: ' + this.name + ', Value: ' + newValue);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return opt_newValue as string;
|
return newValue as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -481,8 +481,8 @@ export class FieldDropdown extends Field<string> {
|
|||||||
this.imageElement_!.style.display = '';
|
this.imageElement_!.style.display = '';
|
||||||
this.imageElement_!.setAttributeNS(
|
this.imageElement_!.setAttributeNS(
|
||||||
dom.XLINK_NS, 'xlink:href', imageJson.src);
|
dom.XLINK_NS, 'xlink:href', imageJson.src);
|
||||||
this.imageElement_!.setAttribute('height', `${imageJson.height}`);
|
this.imageElement_!.setAttribute('height', String(imageJson.height));
|
||||||
this.imageElement_!.setAttribute('width', `${imageJson.width}`);
|
this.imageElement_!.setAttribute('width', String(imageJson.width));
|
||||||
|
|
||||||
const imageHeight = Number(imageJson.height);
|
const imageHeight = Number(imageJson.height);
|
||||||
const imageWidth = Number(imageJson.width);
|
const imageWidth = Number(imageJson.width);
|
||||||
@@ -512,14 +512,13 @@ export class FieldDropdown extends Field<string> {
|
|||||||
let arrowX = 0;
|
let arrowX = 0;
|
||||||
if (block.RTL) {
|
if (block.RTL) {
|
||||||
const imageX = xPadding + arrowWidth;
|
const imageX = xPadding + arrowWidth;
|
||||||
this.imageElement_!.setAttribute('x', imageX.toString());
|
this.imageElement_!.setAttribute('x', `${imageX}`);
|
||||||
} else {
|
} else {
|
||||||
arrowX = imageWidth + arrowWidth;
|
arrowX = imageWidth + arrowWidth;
|
||||||
this.getTextElement().setAttribute('text-anchor', 'end');
|
this.getTextElement().setAttribute('text-anchor', 'end');
|
||||||
this.imageElement_!.setAttribute('x', xPadding.toString());
|
this.imageElement_!.setAttribute('x', `${xPadding}`);
|
||||||
}
|
}
|
||||||
this.imageElement_!.setAttribute(
|
this.imageElement_!.setAttribute('y', String(height / 2 - imageHeight / 2));
|
||||||
'y', (height / 2 - imageHeight / 2).toString());
|
|
||||||
|
|
||||||
this.positionTextElement_(arrowX + xPadding, imageWidth + arrowWidth);
|
this.positionTextElement_(arrowX + xPadding, imageWidth + arrowWidth);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,19 +64,19 @@ export class FieldImage extends Field<string> {
|
|||||||
* after their own constructors have run).
|
* after their own constructors have run).
|
||||||
* @param width Width of the image.
|
* @param width Width of the image.
|
||||||
* @param height Height of the image.
|
* @param height Height of the image.
|
||||||
* @param opt_alt Optional alt text for when block is collapsed.
|
* @param alt Optional alt text for when block is collapsed.
|
||||||
* @param opt_onClick Optional function to be called when the image is
|
* @param onClick Optional function to be called when the image is
|
||||||
* clicked. If opt_onClick is defined, opt_alt must also be defined.
|
* clicked. If onClick is defined, alt must also be defined.
|
||||||
* @param opt_flipRtl Whether to flip the icon in RTL.
|
* @param flipRtl Whether to flip the icon in RTL.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/image#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/image#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
src: string|Sentinel, width: string|number, height: string|number,
|
src: string|Sentinel, width: string|number, height: string|number,
|
||||||
opt_alt?: string, opt_onClick?: (p1: FieldImage) => void,
|
alt?: string, onClick?: (p1: FieldImage) => void, flipRtl?: boolean,
|
||||||
opt_flipRtl?: boolean, opt_config?: FieldImageConfig) {
|
config?: FieldImageConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
const imageHeight = Number(parsing.replaceMessageReferences(height));
|
const imageHeight = Number(parsing.replaceMessageReferences(height));
|
||||||
@@ -100,19 +100,19 @@ export class FieldImage extends Field<string> {
|
|||||||
*/
|
*/
|
||||||
this.imageHeight_ = imageHeight;
|
this.imageHeight_ = imageHeight;
|
||||||
|
|
||||||
if (typeof opt_onClick === 'function') {
|
if (typeof onClick === 'function') {
|
||||||
this.clickHandler_ = opt_onClick;
|
this.clickHandler_ = onClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src === Field.SKIP_SETUP) {
|
if (src === Field.SKIP_SETUP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
} else {
|
} else {
|
||||||
this.flipRtl_ = !!opt_flipRtl;
|
this.flipRtl_ = !!flipRtl;
|
||||||
this.altText_ = parsing.replaceMessageReferences(opt_alt) || '';
|
this.altText_ = parsing.replaceMessageReferences(alt) || '';
|
||||||
}
|
}
|
||||||
this.setValue(parsing.replaceMessageReferences(src));
|
this.setValue(parsing.replaceMessageReferences(src));
|
||||||
}
|
}
|
||||||
@@ -157,14 +157,14 @@ export class FieldImage extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the input value (the source URL) is a string.
|
* Ensure that the input value (the source URL) is a string.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A string, or null if invalid.
|
* @returns A string, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: any): string|null {
|
protected override doClassValidation_(newValue?: any): string|null {
|
||||||
if (typeof opt_newValue !== 'string') {
|
if (typeof newValue !== 'string') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return opt_newValue;
|
return newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,7 +177,7 @@ export class FieldImage extends Field<string> {
|
|||||||
this.value_ = newValue;
|
this.value_ = newValue;
|
||||||
if (this.imageElement_) {
|
if (this.imageElement_) {
|
||||||
this.imageElement_.setAttributeNS(
|
this.imageElement_.setAttributeNS(
|
||||||
dom.XLINK_NS, 'xlink:href', String(this.value_));
|
dom.XLINK_NS, 'xlink:href', this.value_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {Field, FieldConfig, FieldValidator, UnattachedFieldError} from './field.
|
|||||||
import {Msg} from './msg.js';
|
import {Msg} from './msg.js';
|
||||||
import * as aria from './utils/aria.js';
|
import * as aria from './utils/aria.js';
|
||||||
import {Coordinate} from './utils/coordinate.js';
|
import {Coordinate} from './utils/coordinate.js';
|
||||||
import {KeyCodes} from './utils/keycodes.js';
|
|
||||||
import type {Sentinel} from './utils/sentinel.js';
|
import type {Sentinel} from './utils/sentinel.js';
|
||||||
import * as userAgent from './utils/useragent.js';
|
import * as userAgent from './utils/useragent.js';
|
||||||
import * as WidgetDiv from './widgetdiv.js';
|
import * as WidgetDiv from './widgetdiv.js';
|
||||||
@@ -90,31 +89,31 @@ export abstract class FieldInput<T extends InputTypes> extends Field<string|T> {
|
|||||||
override CURSOR = 'text';
|
override CURSOR = 'text';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a string.
|
* @param value The initial value of the field. Should cast to a string.
|
||||||
* Defaults to an empty string if null or undefined. Also accepts
|
* Defaults to an empty string if null or undefined. Also accepts
|
||||||
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
||||||
* that want to handle configuration and setting the field value after
|
* that want to handle configuration and setting the field value after
|
||||||
* their own constructors have run).
|
* their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a string & returns a validated string, or null
|
* field's value. Takes in a string & returns a validated string, or null
|
||||||
* to abort the change.
|
* to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|Sentinel, opt_validator?: FieldInputValidator<T>|null,
|
value?: string|Sentinel, validator?: FieldInputValidator<T>|null,
|
||||||
opt_config?: FieldInputConfig) {
|
config?: FieldInputConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,14 +261,13 @@ export abstract class FieldInput<T extends InputTypes> extends Field<string|T> {
|
|||||||
* Shows a prompt editor for mobile browsers if the modalInputs option is
|
* Shows a prompt editor for mobile browsers if the modalInputs option is
|
||||||
* enabled.
|
* enabled.
|
||||||
*
|
*
|
||||||
* @param _opt_e Optional mouse event that triggered the field to open, or
|
* @param _e Optional mouse event that triggered the field to open, or
|
||||||
* undefined if triggered programmatically.
|
* undefined if triggered programmatically.
|
||||||
* @param opt_quietInput True if editor should be created without focus.
|
* @param quietInput True if editor should be created without focus.
|
||||||
* Defaults to false.
|
* Defaults to false.
|
||||||
*/
|
*/
|
||||||
protected override showEditor_(_opt_e?: Event, opt_quietInput?: boolean) {
|
protected override showEditor_(_e?: Event, quietInput = false) {
|
||||||
this.workspace_ = (this.sourceBlock_ as BlockSvg).workspace;
|
this.workspace_ = (this.sourceBlock_ as BlockSvg).workspace;
|
||||||
const quietInput = opt_quietInput || false;
|
|
||||||
if (!quietInput && this.workspace_.options.modalInputs &&
|
if (!quietInput && this.workspace_.options.modalInputs &&
|
||||||
(userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD)) {
|
(userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD)) {
|
||||||
this.showPromptEditor_();
|
this.showPromptEditor_();
|
||||||
@@ -358,7 +356,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<string|T> {
|
|||||||
div!.style.transition = 'box-shadow 0.25s ease 0s';
|
div!.style.transition = 'box-shadow 0.25s ease 0s';
|
||||||
if (this.getConstants()!.FIELD_TEXTINPUT_BOX_SHADOW) {
|
if (this.getConstants()!.FIELD_TEXTINPUT_BOX_SHADOW) {
|
||||||
div!.style.boxShadow =
|
div!.style.boxShadow =
|
||||||
'rgba(255, 255, 255, 0.3) 0 0 0 ' + 4 * scale + 'px';
|
'rgba(255, 255, 255, 0.3) 0 0 0 ' + (4 * scale) + 'px';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
htmlInput.style.borderRadius = borderRadius;
|
htmlInput.style.borderRadius = borderRadius;
|
||||||
@@ -444,15 +442,15 @@ export abstract class FieldInput<T extends InputTypes> extends Field<string|T> {
|
|||||||
* @param e Keyboard event.
|
* @param e Keyboard event.
|
||||||
*/
|
*/
|
||||||
protected onHtmlInputKeyDown_(e: KeyboardEvent) {
|
protected onHtmlInputKeyDown_(e: KeyboardEvent) {
|
||||||
if (e.keyCode === KeyCodes.ENTER) {
|
if (e.key === 'Enter') {
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
dropDownDiv.hideWithoutAnimation();
|
||||||
} else if (e.keyCode === KeyCodes.ESC) {
|
} else if (e.key === 'Esc') {
|
||||||
this.setValue(
|
this.setValue(
|
||||||
this.htmlInput_!.getAttribute('data-untyped-default-value'));
|
this.htmlInput_!.getAttribute('data-untyped-default-value'));
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
dropDownDiv.hideWithoutAnimation();
|
||||||
} else if (e.keyCode === KeyCodes.TAB) {
|
} else if (e.key === 'Tab') {
|
||||||
WidgetDiv.hide();
|
WidgetDiv.hide();
|
||||||
dropDownDiv.hideWithoutAnimation();
|
dropDownDiv.hideWithoutAnimation();
|
||||||
(this.sourceBlock_ as BlockSvg).tab(this, !e.shiftKey);
|
(this.sourceBlock_ as BlockSvg).tab(this, !e.shiftKey);
|
||||||
@@ -543,7 +541,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<string|T> {
|
|||||||
* @returns The text to show on the HTML input.
|
* @returns The text to show on the HTML input.
|
||||||
*/
|
*/
|
||||||
protected getEditorText_(value: AnyDuringMigration): string {
|
protected getEditorText_(value: AnyDuringMigration): string {
|
||||||
return String(value);
|
return `${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import type {Sentinel} from './utils/sentinel.js';
|
|||||||
* Class for a non-editable, non-serializable text field.
|
* Class for a non-editable, non-serializable text field.
|
||||||
*/
|
*/
|
||||||
export class FieldLabel extends Field<string> {
|
export class FieldLabel extends Field<string> {
|
||||||
/** The html class name to use for this field. */
|
/** The HTML class name to use for this field. */
|
||||||
private class_: string|null = null;
|
private class_: string|null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,29 +33,28 @@ export class FieldLabel extends Field<string> {
|
|||||||
override EDITABLE = false;
|
override EDITABLE = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a string.
|
* @param value The initial value of the field. Should cast to a string.
|
||||||
* Defaults to an empty string if null or undefined. Also accepts
|
* Defaults to an empty string if null or undefined. Also accepts
|
||||||
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
||||||
* that want to handle configuration and setting the field value after
|
* that want to handle configuration and setting the field value after
|
||||||
* their own constructors have run).
|
* their own constructors have run).
|
||||||
* @param opt_class Optional CSS class for the field's text.
|
* @param textClass Optional CSS class for the field's text.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/label#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/label#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|Sentinel, opt_class?: string,
|
value?: string|Sentinel, textClass?: string, config?: FieldLabelConfig) {
|
||||||
opt_config?: FieldLabelConfig) {
|
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
} else {
|
} else {
|
||||||
this.class_ = opt_class || null;
|
this.class_ = textClass || null;
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override configure_(config: FieldLabelConfig) {
|
protected override configure_(config: FieldLabelConfig) {
|
||||||
@@ -78,15 +77,15 @@ export class FieldLabel extends Field<string> {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the input value casts to a valid string.
|
* Ensure that the input value casts to a valid string.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid string, or null if invalid.
|
* @returns A valid string, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
protected override doClassValidation_(newValue?: AnyDuringMigration): string
|
||||||
string|null {
|
|null {
|
||||||
if (opt_newValue === null || opt_newValue === undefined) {
|
if (newValue === null || newValue === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return String(opt_newValue);
|
return `${newValue}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,17 +36,16 @@ export class FieldLabelSerializable extends FieldLabel {
|
|||||||
override SERIALIZABLE = true;
|
override SERIALIZABLE = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a string.
|
* @param value The initial value of the field. Should cast to a string.
|
||||||
* Defaults to an empty string if null or undefined.
|
* Defaults to an empty string if null or undefined.
|
||||||
* @param opt_class Optional CSS class for the field's text.
|
* @param textClass Optional CSS class for the field's text.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/label-serializable#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/label-serializable#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(value?: string, textClass?: string, config?: FieldLabelConfig) {
|
||||||
opt_value?: string, opt_class?: string, opt_config?: FieldLabelConfig) {
|
super(String(value ?? ''), textClass, config);
|
||||||
super(String(opt_value ?? ''), opt_class, opt_config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import * as fieldRegistry from './field_registry.js';
|
|||||||
import {FieldTextInput, FieldTextInputConfig, FieldTextInputValidator} from './field_textinput.js';
|
import {FieldTextInput, FieldTextInputConfig, FieldTextInputValidator} from './field_textinput.js';
|
||||||
import * as aria from './utils/aria.js';
|
import * as aria from './utils/aria.js';
|
||||||
import * as dom from './utils/dom.js';
|
import * as dom from './utils/dom.js';
|
||||||
import {KeyCodes} from './utils/keycodes.js';
|
|
||||||
import * as parsing from './utils/parsing.js';
|
import * as parsing from './utils/parsing.js';
|
||||||
import type {Sentinel} from './utils/sentinel.js';
|
import type {Sentinel} from './utils/sentinel.js';
|
||||||
import {Svg} from './utils/svg.js';
|
import {Svg} from './utils/svg.js';
|
||||||
@@ -33,9 +32,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
* The SVG group element that will contain a text element for each text row
|
* The SVG group element that will contain a text element for each text row
|
||||||
* when initialized.
|
* when initialized.
|
||||||
*/
|
*/
|
||||||
// AnyDuringMigration because: Type 'null' is not assignable to type
|
textGroup: SVGGElement|null = null;
|
||||||
// 'SVGGElement'.
|
|
||||||
textGroup_: SVGGElement = null as AnyDuringMigration;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the maximum number of lines of field.
|
* Defines the maximum number of lines of field.
|
||||||
@@ -47,35 +44,40 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
protected isOverflowedY_ = false;
|
protected isOverflowedY_ = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial content of the field. Should cast to a string.
|
* @param value The initial content of the field. Should cast to a string.
|
||||||
* Defaults to an empty string if null or undefined. Also accepts
|
* Defaults to an empty string if null or undefined. Also accepts
|
||||||
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
||||||
* that want to handle configuration and setting the field value after
|
* that want to handle configuration and setting the field value after
|
||||||
* their own constructors have run).
|
* their own constructors have run).
|
||||||
* @param opt_validator An optional function that is called to validate any
|
* @param validator An optional function that is called to validate any
|
||||||
* constraints on what the user entered. Takes the new text as an
|
* constraints on what the user entered. Takes the new text as an
|
||||||
* argument and returns either the accepted text, a replacement text, or
|
* argument and returns either the accepted text, a replacement text, or
|
||||||
* null to abort the change.
|
* null to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/multiline-text-input#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/multiline-text-input#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|Sentinel, opt_validator?: FieldMultilineInputValidator,
|
value?: string|Sentinel, validator?: FieldMultilineInputValidator,
|
||||||
opt_config?: FieldMultilineInputConfig) {
|
config?: FieldMultilineInputConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the field based on the given map of options.
|
||||||
|
*
|
||||||
|
* @param config A map of options to configure the field based on.
|
||||||
|
*/
|
||||||
protected override configure_(config: FieldMultilineInputConfig) {
|
protected override configure_(config: FieldMultilineInputConfig) {
|
||||||
super.configure_(config);
|
super.configure_(config);
|
||||||
if (config.maxLines) this.setMaxLines(config.maxLines);
|
if (config.maxLines) this.setMaxLines(config.maxLines);
|
||||||
@@ -112,6 +114,8 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves this field's value.
|
* Saves this field's value.
|
||||||
|
* This function only exists for subclasses of FieldMultilineInput which
|
||||||
|
* predate the load/saveState API and only define to/fromXml.
|
||||||
*
|
*
|
||||||
* @returns The state of this field.
|
* @returns The state of this field.
|
||||||
* @internal
|
* @internal
|
||||||
@@ -126,6 +130,8 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the field's value based on the given state.
|
* Sets the field's value based on the given state.
|
||||||
|
* This function only exists for subclasses of FieldMultilineInput which
|
||||||
|
* predate the load/saveState API and only define to/fromXml.
|
||||||
*
|
*
|
||||||
* @param state The state of the variable to assign to this variable field.
|
* @param state The state of the variable to assign to this variable field.
|
||||||
* @internal
|
* @internal
|
||||||
@@ -144,7 +150,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
*/
|
*/
|
||||||
override initView() {
|
override initView() {
|
||||||
this.createBorderRect_();
|
this.createBorderRect_();
|
||||||
this.textGroup_ = dom.createSvgElement(
|
this.textGroup = dom.createSvgElement(
|
||||||
Svg.G, {
|
Svg.G, {
|
||||||
'class': 'blocklyEditableText',
|
'class': 'blocklyEditableText',
|
||||||
},
|
},
|
||||||
@@ -219,8 +225,9 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
}
|
}
|
||||||
// Remove all text group children.
|
// Remove all text group children.
|
||||||
let currentChild;
|
let currentChild;
|
||||||
while (currentChild = this.textGroup_.firstChild) {
|
const textGroup = this.textGroup;
|
||||||
this.textGroup_.removeChild(currentChild);
|
while (currentChild = textGroup!.firstChild) {
|
||||||
|
textGroup!.removeChild(currentChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add in text elements into the group.
|
// Add in text elements into the group.
|
||||||
@@ -236,7 +243,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
'y': y + this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING,
|
'y': y + this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING,
|
||||||
'dy': this.getConstants()!.FIELD_TEXT_BASELINE,
|
'dy': this.getConstants()!.FIELD_TEXT_BASELINE,
|
||||||
},
|
},
|
||||||
this.textGroup_);
|
textGroup);
|
||||||
span.appendChild(document.createTextNode(lines[i]));
|
span.appendChild(document.createTextNode(lines[i]));
|
||||||
y += lineHeight;
|
y += lineHeight;
|
||||||
}
|
}
|
||||||
@@ -274,7 +281,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
|
|
||||||
/** Updates the size of the field based on the text. */
|
/** Updates the size of the field based on the text. */
|
||||||
protected override updateSize_() {
|
protected override updateSize_() {
|
||||||
const nodes = this.textGroup_.childNodes;
|
const nodes = (this.textGroup as SVGElement).childNodes;
|
||||||
const fontSize = this.getConstants()!.FIELD_TEXT_FONTSIZE;
|
const fontSize = this.getConstants()!.FIELD_TEXT_FONTSIZE;
|
||||||
const fontWeight = this.getConstants()!.FIELD_TEXT_FONTWEIGHT;
|
const fontWeight = this.getConstants()!.FIELD_TEXT_FONTWEIGHT;
|
||||||
const fontFamily = this.getConstants()!.FIELD_TEXT_FONTFAMILY;
|
const fontFamily = this.getConstants()!.FIELD_TEXT_FONTFAMILY;
|
||||||
@@ -320,13 +327,8 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
if (this.borderRect_) {
|
if (this.borderRect_) {
|
||||||
totalHeight += this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING * 2;
|
totalHeight += this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING * 2;
|
||||||
totalWidth += this.getConstants()!.FIELD_BORDER_RECT_X_PADDING * 2;
|
totalWidth += this.getConstants()!.FIELD_BORDER_RECT_X_PADDING * 2;
|
||||||
// AnyDuringMigration because: Argument of type 'number' is not
|
this.borderRect_.setAttribute('width', `${totalWidth}`);
|
||||||
// assignable to parameter of type 'string'.
|
this.borderRect_.setAttribute('height', `${totalHeight}`);
|
||||||
this.borderRect_.setAttribute('width', totalWidth as AnyDuringMigration);
|
|
||||||
// AnyDuringMigration because: Argument of type 'number' is not
|
|
||||||
// assignable to parameter of type 'string'.
|
|
||||||
this.borderRect_.setAttribute(
|
|
||||||
'height', totalHeight as AnyDuringMigration);
|
|
||||||
}
|
}
|
||||||
this.size_.width = totalWidth;
|
this.size_.width = totalWidth;
|
||||||
this.size_.height = totalHeight;
|
this.size_.height = totalHeight;
|
||||||
@@ -339,13 +341,13 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
* Overrides the default behaviour to force rerender in order to
|
* Overrides the default behaviour to force rerender in order to
|
||||||
* correct block size, based on editor text.
|
* correct block size, based on editor text.
|
||||||
*
|
*
|
||||||
* @param _opt_e Optional mouse event that triggered the field to open, or
|
* @param e Optional mouse event that triggered the field to open, or
|
||||||
* undefined if triggered programmatically.
|
* undefined if triggered programmatically.
|
||||||
* @param opt_quietInput True if editor should be created without focus.
|
* @param quietInput True if editor should be created without focus.
|
||||||
* Defaults to false.
|
* Defaults to false.
|
||||||
*/
|
*/
|
||||||
override showEditor_(_opt_e?: Event, opt_quietInput?: boolean) {
|
override showEditor_(e?: Event, quietInput?: boolean) {
|
||||||
super.showEditor_(_opt_e, opt_quietInput);
|
super.showEditor_(e, quietInput);
|
||||||
this.forceRerender();
|
this.forceRerender();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,10 +362,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
|
|
||||||
const htmlInput = (document.createElement('textarea'));
|
const htmlInput = (document.createElement('textarea'));
|
||||||
htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput';
|
htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput';
|
||||||
// AnyDuringMigration because: Argument of type 'boolean' is not assignable
|
htmlInput.setAttribute('spellcheck', String(this.spellcheck_));
|
||||||
// to parameter of type 'string'.
|
|
||||||
htmlInput.setAttribute(
|
|
||||||
'spellcheck', this.spellcheck_ as AnyDuringMigration);
|
|
||||||
const fontSize = this.getConstants()!.FIELD_TEXT_FONTSIZE * scale + 'pt';
|
const fontSize = this.getConstants()!.FIELD_TEXT_FONTSIZE * scale + 'pt';
|
||||||
div!.style.fontSize = fontSize;
|
div!.style.fontSize = fontSize;
|
||||||
htmlInput.style.fontSize = fontSize;
|
htmlInput.style.fontSize = fontSize;
|
||||||
@@ -425,7 +424,7 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
* @param e Keyboard event.
|
* @param e Keyboard event.
|
||||||
*/
|
*/
|
||||||
protected override onHtmlInputKeyDown_(e: KeyboardEvent) {
|
protected override onHtmlInputKeyDown_(e: KeyboardEvent) {
|
||||||
if (e.keyCode !== KeyCodes.ENTER) {
|
if (e.key !== 'Enter') {
|
||||||
super.onHtmlInputKeyDown_(e);
|
super.onHtmlInputKeyDown_(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,7 +447,12 @@ export class FieldMultilineInput extends FieldTextInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** CSS for multiline field. See css.js for use. */
|
fieldRegistry.register('field_multilinetext', FieldMultilineInput);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS for multiline field.
|
||||||
|
*/
|
||||||
Css.register(`
|
Css.register(`
|
||||||
.blocklyHtmlTextAreaInput {
|
.blocklyHtmlTextAreaInput {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
@@ -463,8 +467,6 @@ Css.register(`
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
fieldRegistry.register('field_multilinetext', FieldMultilineInput);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config options for the multiline input field.
|
* Config options for the multiline input field.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,51 +37,44 @@ export class FieldNumber extends FieldInput<number> {
|
|||||||
*/
|
*/
|
||||||
private decimalPlaces_: number|null = null;
|
private decimalPlaces_: number|null = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializable fields are saved by the serializer, non-serializable fields
|
|
||||||
* are not. Editable fields should also be serializable.
|
|
||||||
*/
|
|
||||||
override SERIALIZABLE = true;
|
|
||||||
|
|
||||||
/** Don't spellcheck numbers. Our validator does a better job. */
|
/** Don't spellcheck numbers. Our validator does a better job. */
|
||||||
protected override spellcheck_ = false;
|
protected override spellcheck_ = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a number.
|
* @param value The initial value of the field. Should cast to a number.
|
||||||
* Defaults to 0. Also accepts Field.SKIP_SETUP if you wish to skip setup
|
* Defaults to 0. Also accepts Field.SKIP_SETUP if you wish to skip setup
|
||||||
* (only used by subclasses that want to handle configuration and setting
|
* (only used by subclasses that want to handle configuration and setting
|
||||||
* the field value after their own constructors have run).
|
* the field value after their own constructors have run).
|
||||||
* @param opt_min Minimum value. Will only be used if opt_config is not
|
* @param min Minimum value. Will only be used if config is not
|
||||||
* provided.
|
* provided.
|
||||||
* @param opt_max Maximum value. Will only be used if opt_config is not
|
* @param max Maximum value. Will only be used if config is not
|
||||||
* provided.
|
* provided.
|
||||||
* @param opt_precision Precision for value. Will only be used if opt_config
|
* @param precision Precision for value. Will only be used if config
|
||||||
* is not provided.
|
* is not provided.
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a number & returns a validated number, or null
|
* field's value. Takes in a number & returns a validated number, or null
|
||||||
* to abort the change.
|
* to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|number|Sentinel, opt_min?: string|number|null,
|
value?: string|number|Sentinel, min?: string|number|null,
|
||||||
opt_max?: string|number|null, opt_precision?: string|number|null,
|
max?: string|number|null, precision?: string|number|null,
|
||||||
opt_validator?: FieldNumberValidator|null,
|
validator?: FieldNumberValidator|null, config?: FieldNumberConfig) {
|
||||||
opt_config?: FieldNumberConfig) {
|
|
||||||
// Pass SENTINEL so that we can define properties before value validation.
|
// Pass SENTINEL so that we can define properties before value validation.
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
if (Field.isSentinel(opt_value)) return;
|
if (Field.isSentinel(value)) return;
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
} else {
|
} else {
|
||||||
this.setConstraints(opt_min, opt_max, opt_precision);
|
this.setConstraints(min, max, precision);
|
||||||
}
|
}
|
||||||
this.setValue(opt_value);
|
this.setValue(value);
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,17 +239,17 @@ export class FieldNumber extends FieldInput<number> {
|
|||||||
* Ensure that the input value is a valid number (must fulfill the
|
* Ensure that the input value is a valid number (must fulfill the
|
||||||
* constraints placed on the field).
|
* constraints placed on the field).
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid number, or null if invalid.
|
* @returns A valid number, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
protected override doClassValidation_(newValue?: AnyDuringMigration): number
|
||||||
number|null {
|
|null {
|
||||||
if (opt_newValue === null) {
|
if (newValue === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up text.
|
// Clean up text.
|
||||||
let newValue = String(opt_newValue);
|
newValue = `${newValue}`;
|
||||||
// TODO: Handle cases like 'ten', '1.203,14', etc.
|
// TODO: Handle cases like 'ten', '1.203,14', etc.
|
||||||
// 'O' is sometimes mistaken for '0' by inexperienced users.
|
// 'O' is sometimes mistaken for '0' by inexperienced users.
|
||||||
newValue = newValue.replace(/O/ig, '0');
|
newValue = newValue.replace(/O/ig, '0');
|
||||||
|
|||||||
@@ -25,37 +25,37 @@ import type {Sentinel} from './utils/sentinel.js';
|
|||||||
*/
|
*/
|
||||||
export class FieldTextInput extends FieldInput<string> {
|
export class FieldTextInput extends FieldInput<string> {
|
||||||
/**
|
/**
|
||||||
* @param opt_value The initial value of the field. Should cast to a string.
|
* @param value The initial value of the field. Should cast to a string.
|
||||||
* Defaults to an empty string if null or undefined. Also accepts
|
* Defaults to an empty string if null or undefined. Also accepts
|
||||||
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
* Field.SKIP_SETUP if you wish to skip setup (only used by subclasses
|
||||||
* that want to handle configuration and setting the field value after
|
* that want to handle configuration and setting the field value after
|
||||||
* their own constructors have run).
|
* their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a string & returns a validated string, or null
|
* field's value. Takes in a string & returns a validated string, or null
|
||||||
* to abort the change.
|
* to abort the change.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
opt_value?: string|Sentinel, opt_validator?: FieldTextInputValidator|null,
|
value?: string|Sentinel, validator?: FieldTextInputValidator|null,
|
||||||
opt_config?: FieldTextInputConfig) {
|
config?: FieldTextInputConfig) {
|
||||||
super(opt_value, opt_validator, opt_config);
|
super(value, validator, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that the input value casts to a valid string.
|
* Ensure that the input value casts to a valid string.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The input value.
|
* @param newValue The input value.
|
||||||
* @returns A valid string, or null if invalid.
|
* @returns A valid string, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
protected override doClassValidation_(newValue?: AnyDuringMigration): string
|
||||||
string|null {
|
|null {
|
||||||
if (opt_newValue === undefined) {
|
if (newValue === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return String(opt_newValue);
|
return `${newValue}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -62,23 +62,23 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
* Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by
|
* Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by
|
||||||
* subclasses that want to handle configuration and setting the field value
|
* subclasses that want to handle configuration and setting the field value
|
||||||
* after their own constructors have run).
|
* after their own constructors have run).
|
||||||
* @param opt_validator A function that is called to validate changes to the
|
* @param validator A function that is called to validate changes to the
|
||||||
* field's value. Takes in a variable ID & returns a validated variable
|
* field's value. Takes in a variable ID & returns a validated variable
|
||||||
* ID, or null to abort the change.
|
* ID, or null to abort the change.
|
||||||
* @param opt_variableTypes A list of the types of variables to include in the
|
* @param variableTypes A list of the types of variables to include in the
|
||||||
* dropdown. Will only be used if opt_config is not provided.
|
* dropdown. Will only be used if config is not provided.
|
||||||
* @param opt_defaultType The type of variable to create if this field's value
|
* @param defaultType The type of variable to create if this field's value
|
||||||
* is not explicitly set. Defaults to ''. Will only be used if opt_config
|
* is not explicitly set. Defaults to ''. Will only be used if config
|
||||||
* is not provided.
|
* is not provided.
|
||||||
* @param opt_config A map of options used to configure the field.
|
* @param config A map of options used to configure the field.
|
||||||
* See the [field creation documentation]{@link
|
* See the [field creation documentation]{@link
|
||||||
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/variable#creation}
|
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/variable#creation}
|
||||||
* for a list of properties this parameter supports.
|
* for a list of properties this parameter supports.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
varName: string|null|Sentinel, opt_validator?: FieldVariableValidator,
|
varName: string|null|Sentinel, validator?: FieldVariableValidator,
|
||||||
opt_variableTypes?: string[], opt_defaultType?: string,
|
variableTypes?: string[], defaultType?: string,
|
||||||
opt_config?: FieldVariableConfig) {
|
config?: FieldVariableConfig) {
|
||||||
super(Field.SKIP_SETUP);
|
super(Field.SKIP_SETUP);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,13 +101,13 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_config) {
|
if (config) {
|
||||||
this.configure_(opt_config);
|
this.configure_(config);
|
||||||
} else {
|
} else {
|
||||||
this.setTypes_(opt_variableTypes, opt_defaultType);
|
this.setTypes_(variableTypes, defaultType);
|
||||||
}
|
}
|
||||||
if (opt_validator) {
|
if (validator) {
|
||||||
this.setValidator(opt_validator);
|
this.setValidator(validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,19 +315,19 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
/**
|
/**
|
||||||
* Ensure that the ID belongs to a valid variable of an allowed type.
|
* Ensure that the ID belongs to a valid variable of an allowed type.
|
||||||
*
|
*
|
||||||
* @param opt_newValue The ID of the new variable to set.
|
* @param newValue The ID of the new variable to set.
|
||||||
* @returns The validated ID, or null if invalid.
|
* @returns The validated ID, or null if invalid.
|
||||||
*/
|
*/
|
||||||
protected override doClassValidation_(opt_newValue?: AnyDuringMigration):
|
protected override doClassValidation_(newValue?: AnyDuringMigration): string
|
||||||
string|null {
|
|null {
|
||||||
if (opt_newValue === null) {
|
if (newValue === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const block = this.getSourceBlock();
|
const block = this.getSourceBlock();
|
||||||
if (!block) {
|
if (!block) {
|
||||||
throw new UnattachedFieldError();
|
throw new UnattachedFieldError();
|
||||||
}
|
}
|
||||||
const newId = opt_newValue as string;
|
const newId = newValue as string;
|
||||||
const variable = Variables.getVariable(block.workspace, newId);
|
const variable = Variables.getVariable(block.workspace, newId);
|
||||||
if (!variable) {
|
if (!variable) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@@ -410,23 +410,19 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
* Parse the optional arguments representing the allowed variable types and
|
* Parse the optional arguments representing the allowed variable types and
|
||||||
* the default variable type.
|
* the default variable type.
|
||||||
*
|
*
|
||||||
* @param opt_variableTypes A list of the types of variables to include in the
|
* @param variableTypes A list of the types of variables to include in the
|
||||||
* dropdown. If null or undefined, variables of all types will be
|
* dropdown. If null or undefined, variables of all types will be
|
||||||
* displayed in the dropdown.
|
* displayed in the dropdown.
|
||||||
* @param opt_defaultType The type of the variable to create if this field's
|
* @param defaultType The type of the variable to create if this field's
|
||||||
* value is not explicitly set. Defaults to ''.
|
* value is not explicitly set. Defaults to ''.
|
||||||
*/
|
*/
|
||||||
private setTypes_(opt_variableTypes?: string[], opt_defaultType?: string) {
|
private setTypes_(variableTypes: string[]|null = null, defaultType = '') {
|
||||||
// If you expected that the default type would be the same as the only entry
|
// If you expected that the default type would be the same as the only entry
|
||||||
// in the variable types array, tell the Blockly team by commenting on
|
// in the variable types array, tell the Blockly team by commenting on
|
||||||
// #1499.
|
// #1499.
|
||||||
const defaultType = opt_defaultType || '';
|
|
||||||
let variableTypes;
|
|
||||||
// Set the allowable variable types. Null means all types on the workspace.
|
// Set the allowable variable types. Null means all types on the workspace.
|
||||||
if (opt_variableTypes === null || opt_variableTypes === undefined) {
|
if (Array.isArray(variableTypes)) {
|
||||||
variableTypes = null;
|
variableTypes = variableTypes;
|
||||||
} else if (Array.isArray(opt_variableTypes)) {
|
|
||||||
variableTypes = opt_variableTypes;
|
|
||||||
// Make sure the default type is valid.
|
// Make sure the default type is valid.
|
||||||
let isInArray = false;
|
let isInArray = false;
|
||||||
for (let i = 0; i < variableTypes.length; i++) {
|
for (let i = 0; i < variableTypes.length; i++) {
|
||||||
@@ -439,7 +435,7 @@ export class FieldVariable extends FieldDropdown {
|
|||||||
'Invalid default type \'' + defaultType + '\' in ' +
|
'Invalid default type \'' + defaultType + '\' in ' +
|
||||||
'the definition of a FieldVariable');
|
'the definition of a FieldVariable');
|
||||||
}
|
}
|
||||||
} else {
|
} else if (variableTypes !== null) {
|
||||||
throw Error(
|
throw Error(
|
||||||
'\'variableTypes\' was not an array in the definition of ' +
|
'\'variableTypes\' was not an array in the definition of ' +
|
||||||
'a FieldVariable');
|
'a FieldVariable');
|
||||||
|
|||||||
@@ -74,8 +74,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
/**
|
/**
|
||||||
* Lay out the blocks in the flyout.
|
* Lay out the blocks in the flyout.
|
||||||
*
|
*
|
||||||
* @param contents The blocks and buttons to lay
|
* @param contents The blocks and buttons to lay out.
|
||||||
* out.
|
|
||||||
* @param gaps The visible gaps between blocks.
|
* @param gaps The visible gaps between blocks.
|
||||||
*/
|
*/
|
||||||
protected abstract layout_(contents: FlyoutItem[], gaps: number[]): void;
|
protected abstract layout_(contents: FlyoutItem[], gaps: number[]): void;
|
||||||
@@ -128,9 +127,11 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
protected toolboxPosition_: number;
|
protected toolboxPosition_: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opaque data that can be passed to Blockly.unbindEvent_.
|
* Array holding info needed to unbind events.
|
||||||
|
* Used for disposing.
|
||||||
|
* Ex: [[node, name, func], [node, name, func]].
|
||||||
*/
|
*/
|
||||||
private eventWrappers_: browserEvents.Data = [];
|
private boundEvents: browserEvents.Data[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that will be registered as a change listener on the workspace
|
* Function that will be registered as a change listener on the workspace
|
||||||
@@ -357,21 +358,17 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
Array.prototype.push.apply(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
this.eventWrappers_,
|
(this.svgGroup_ as SVGGElement), 'wheel', this, this.wheel_));
|
||||||
browserEvents.conditionalBind(
|
|
||||||
(this.svgGroup_ as SVGGElement), 'wheel', this, this.wheel_));
|
|
||||||
if (!this.autoClose) {
|
if (!this.autoClose) {
|
||||||
this.filterWrapper_ = this.filterForCapacity_.bind(this);
|
this.filterWrapper_ = this.filterForCapacity_.bind(this);
|
||||||
this.targetWorkspace.addChangeListener(this.filterWrapper_);
|
this.targetWorkspace.addChangeListener(this.filterWrapper_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dragging the flyout up and down.
|
// Dragging the flyout up and down.
|
||||||
Array.prototype.push.apply(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
this.eventWrappers_,
|
(this.svgBackground_ as SVGPathElement), 'pointerdown', this,
|
||||||
browserEvents.conditionalBind(
|
this.onMouseDown_));
|
||||||
(this.svgBackground_ as SVGPathElement), 'pointerdown', this,
|
|
||||||
this.onMouseDown_));
|
|
||||||
|
|
||||||
// A flyout connected to a workspace doesn't have its own current gesture.
|
// A flyout connected to a workspace doesn't have its own current gesture.
|
||||||
this.workspace_.getGesture =
|
this.workspace_.getGesture =
|
||||||
@@ -401,7 +398,10 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
dispose() {
|
dispose() {
|
||||||
this.hide();
|
this.hide();
|
||||||
this.workspace_.getComponentManager().removeComponent(this.id);
|
this.workspace_.getComponentManager().removeComponent(this.id);
|
||||||
browserEvents.unbind(this.eventWrappers_);
|
for (const event of this.boundEvents) {
|
||||||
|
browserEvents.unbind(event);
|
||||||
|
}
|
||||||
|
this.boundEvents.length = 0;
|
||||||
if (this.filterWrapper_) {
|
if (this.filterWrapper_) {
|
||||||
this.targetWorkspace.removeChangeListener(this.filterWrapper_);
|
this.targetWorkspace.removeChangeListener(this.filterWrapper_);
|
||||||
this.filterWrapper_ = null;
|
this.filterWrapper_ = null;
|
||||||
@@ -525,8 +525,8 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
* @param y The computed y origin of the flyout's SVG group.
|
* @param y The computed y origin of the flyout's SVG group.
|
||||||
*/
|
*/
|
||||||
protected positionAt_(width: number, height: number, x: number, y: number) {
|
protected positionAt_(width: number, height: number, x: number, y: number) {
|
||||||
this.svgGroup_?.setAttribute('width', width.toString());
|
this.svgGroup_?.setAttribute('width', `${width}`);
|
||||||
this.svgGroup_?.setAttribute('height', height.toString());
|
this.svgGroup_?.setAttribute('height', `${height}`);
|
||||||
this.workspace_.setCachedParentSvgSize(width, height);
|
this.workspace_.setCachedParentSvgSize(width, height);
|
||||||
|
|
||||||
if (this.svgGroup_) {
|
if (this.svgGroup_) {
|
||||||
@@ -562,7 +562,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
}
|
}
|
||||||
this.setVisible(false);
|
this.setVisible(false);
|
||||||
// Delete all the event listeners.
|
// Delete all the event listeners.
|
||||||
for (let i = 0, listen; listen = this.listeners_[i]; i++) {
|
for (const listen of this.listeners_) {
|
||||||
browserEvents.unbind(listen);
|
browserEvents.unbind(listen);
|
||||||
}
|
}
|
||||||
this.listeners_.length = 0;
|
this.listeners_.length = 0;
|
||||||
@@ -781,7 +781,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
blockInfo: toolbox.BlockInfo, gaps: number[], defaultGap: number) {
|
blockInfo: toolbox.BlockInfo, gaps: number[], defaultGap: number) {
|
||||||
let gap;
|
let gap;
|
||||||
if (blockInfo['gap']) {
|
if (blockInfo['gap']) {
|
||||||
gap = parseInt(blockInfo['gap'].toString());
|
gap = parseInt(String(blockInfo['gap']));
|
||||||
} else if (blockInfo['blockxml']) {
|
} else if (blockInfo['blockxml']) {
|
||||||
const xml = (typeof blockInfo['blockxml'] === 'string' ?
|
const xml = (typeof blockInfo['blockxml'] === 'string' ?
|
||||||
utilsXml.textToDom(blockInfo['blockxml']) :
|
utilsXml.textToDom(blockInfo['blockxml']) :
|
||||||
@@ -806,7 +806,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
// <sep gap="36"></sep>
|
// <sep gap="36"></sep>
|
||||||
// The default gap is 24, can be set larger or smaller.
|
// The default gap is 24, can be set larger or smaller.
|
||||||
// This overwrites the gap attribute on the previous element.
|
// This overwrites the gap attribute on the previous element.
|
||||||
const newGap = parseInt(sepInfo['gap']!.toString());
|
const newGap = parseInt(String(sepInfo['gap']));
|
||||||
// Ignore gaps before the first block.
|
// Ignore gaps before the first block.
|
||||||
if (!isNaN(newGap) && gaps.length > 0) {
|
if (!isNaN(newGap) && gaps.length > 0) {
|
||||||
gaps[gaps.length - 1] = newGap;
|
gaps[gaps.length - 1] = newGap;
|
||||||
@@ -1056,13 +1056,13 @@ export abstract class Flyout extends DeleteArea implements IFlyout {
|
|||||||
*/
|
*/
|
||||||
protected moveRectToBlock_(rect: SVGElement, block: BlockSvg) {
|
protected moveRectToBlock_(rect: SVGElement, block: BlockSvg) {
|
||||||
const blockHW = block.getHeightWidth();
|
const blockHW = block.getHeightWidth();
|
||||||
rect.setAttribute('width', blockHW.width.toString());
|
rect.setAttribute('width', String(blockHW.width));
|
||||||
rect.setAttribute('height', blockHW.height.toString());
|
rect.setAttribute('height', String(blockHW.height));
|
||||||
|
|
||||||
const blockXY = block.getRelativeToSurfaceXY();
|
const blockXY = block.getRelativeToSurfaceXY();
|
||||||
rect.setAttribute('y', blockXY.y.toString());
|
rect.setAttribute('y', String(blockXY.y));
|
||||||
rect.setAttribute(
|
rect.setAttribute(
|
||||||
'x', (this.RTL ? blockXY.x - blockHW.width : blockXY.x).toString());
|
'x', String(this.RTL ? blockXY.x - blockHW.width : blockXY.x));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -155,17 +155,17 @@ export class FlyoutButton {
|
|||||||
if (!this.isLabel_) {
|
if (!this.isLabel_) {
|
||||||
this.width += 2 * FlyoutButton.TEXT_MARGIN_X;
|
this.width += 2 * FlyoutButton.TEXT_MARGIN_X;
|
||||||
this.height += 2 * FlyoutButton.TEXT_MARGIN_Y;
|
this.height += 2 * FlyoutButton.TEXT_MARGIN_Y;
|
||||||
shadow?.setAttribute('width', this.width.toString());
|
shadow?.setAttribute('width', String(this.width));
|
||||||
shadow?.setAttribute('height', this.height.toString());
|
shadow?.setAttribute('height', String(this.height));
|
||||||
}
|
}
|
||||||
rect.setAttribute('width', this.width.toString());
|
rect.setAttribute('width', String(this.width));
|
||||||
rect.setAttribute('height', this.height.toString());
|
rect.setAttribute('height', String(this.height));
|
||||||
|
|
||||||
svgText.setAttribute('x', (this.width / 2).toString());
|
svgText.setAttribute('x', String(this.width / 2));
|
||||||
svgText.setAttribute(
|
svgText.setAttribute(
|
||||||
'y',
|
'y',
|
||||||
(this.height / 2 - fontMetrics.height / 2 + fontMetrics.baseline)
|
String(
|
||||||
.toString());
|
this.height / 2 - fontMetrics.height / 2 + fontMetrics.baseline));
|
||||||
|
|
||||||
this.updateTransform_();
|
this.updateTransform_();
|
||||||
|
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ export class CodeGenerator {
|
|||||||
if (!Array.isArray(tuple)) {
|
if (!Array.isArray(tuple)) {
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
`Expecting tuple from value block: ${targetBlock.type} See ` +
|
`Expecting tuple from value block: ${targetBlock.type} See ` +
|
||||||
`https://developers.google.com/blockly/guides/create-custom-blocks/generating-code` +
|
`developers.google.com/blockly/guides/create-custom-blocks/generating-code ` +
|
||||||
`for more information`);
|
`for more information`);
|
||||||
}
|
}
|
||||||
let code = tuple[0];
|
let code = tuple[0];
|
||||||
|
|||||||
@@ -102,16 +102,11 @@ export class Gesture {
|
|||||||
private hasExceededDragRadius_ = false;
|
private hasExceededDragRadius_ = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A handle to use to unbind a pointermove listener at the end of a drag.
|
* Array holding info needed to unbind events.
|
||||||
* Opaque data returned from Blockly.bindEventWithChecks_.
|
* Used for disposing.
|
||||||
|
* Ex: [[node, name, func], [node, name, func]].
|
||||||
*/
|
*/
|
||||||
protected onMoveWrapper_: browserEvents.Data|null = null;
|
private boundEvents: browserEvents.Data[] = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* A handle to use to unbind a pointerup listener at the end of a drag.
|
|
||||||
* Opaque data returned from Blockly.bindEventWithChecks_.
|
|
||||||
*/
|
|
||||||
protected onUpWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** The object tracking a bubble drag, or null if none is in progress. */
|
/** The object tracking a bubble drag, or null if none is in progress. */
|
||||||
private bubbleDragger_: BubbleDragger|null = null;
|
private bubbleDragger_: BubbleDragger|null = null;
|
||||||
@@ -158,13 +153,6 @@ export class Gesture {
|
|||||||
/** The starting distance between two touch points. */
|
/** The starting distance between two touch points. */
|
||||||
private startDistance_ = 0;
|
private startDistance_ = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* A handle to use to unbind the second pointerdown listener
|
|
||||||
* at the end of a drag.
|
|
||||||
* Opaque data returned from Blockly.bindEventWithChecks_.
|
|
||||||
*/
|
|
||||||
private onStartWrapper_: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** Boolean for whether or not the workspace supports pinch-zoom. */
|
/** Boolean for whether or not the workspace supports pinch-zoom. */
|
||||||
private isPinchZoomEnabled_: boolean|null = null;
|
private isPinchZoomEnabled_: boolean|null = null;
|
||||||
|
|
||||||
@@ -209,12 +197,10 @@ export class Gesture {
|
|||||||
// Clear the owner's reference to this gesture.
|
// Clear the owner's reference to this gesture.
|
||||||
this.creatorWorkspace.clearGesture();
|
this.creatorWorkspace.clearGesture();
|
||||||
|
|
||||||
if (this.onMoveWrapper_) {
|
for (const event of this.boundEvents) {
|
||||||
browserEvents.unbind(this.onMoveWrapper_);
|
browserEvents.unbind(event);
|
||||||
}
|
|
||||||
if (this.onUpWrapper_) {
|
|
||||||
browserEvents.unbind(this.onUpWrapper_);
|
|
||||||
}
|
}
|
||||||
|
this.boundEvents.length = 0;
|
||||||
|
|
||||||
if (this.blockDragger_) {
|
if (this.blockDragger_) {
|
||||||
this.blockDragger_.dispose();
|
this.blockDragger_.dispose();
|
||||||
@@ -222,10 +208,6 @@ export class Gesture {
|
|||||||
if (this.workspaceDragger_) {
|
if (this.workspaceDragger_) {
|
||||||
this.workspaceDragger_.dispose();
|
this.workspaceDragger_.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.onStartWrapper_) {
|
|
||||||
browserEvents.unbind(this.onStartWrapper_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -511,15 +493,15 @@ export class Gesture {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
bindMouseEvents(e: PointerEvent) {
|
bindMouseEvents(e: PointerEvent) {
|
||||||
this.onStartWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
document, 'pointerdown', null, this.handleStart.bind(this),
|
document, 'pointerdown', null, this.handleStart.bind(this),
|
||||||
/* opt_noCaptureIdentifier */ true);
|
/* opt_noCaptureIdentifier */ true));
|
||||||
this.onMoveWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
document, 'pointermove', null, this.handleMove.bind(this),
|
document, 'pointermove', null, this.handleMove.bind(this),
|
||||||
/* opt_noCaptureIdentifier */ true);
|
/* opt_noCaptureIdentifier */ true));
|
||||||
this.onUpWrapper_ = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
document, 'pointerup', null, this.handleUp.bind(this),
|
document, 'pointerup', null, this.handleUp.bind(this),
|
||||||
/* opt_noCaptureIdentifier */ true);
|
/* opt_noCaptureIdentifier */ true));
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
18
core/grid.ts
18
core/grid.ts
@@ -91,8 +91,8 @@ export class Grid {
|
|||||||
update(scale: number) {
|
update(scale: number) {
|
||||||
const safeSpacing = this.spacing * scale;
|
const safeSpacing = this.spacing * scale;
|
||||||
|
|
||||||
this.pattern.setAttribute('width', safeSpacing.toString());
|
this.pattern.setAttribute('width', `${safeSpacing}`);
|
||||||
this.pattern.setAttribute('height', safeSpacing.toString());
|
this.pattern.setAttribute('height', `${safeSpacing}`);
|
||||||
|
|
||||||
let half = Math.floor(this.spacing / 2) + 0.5;
|
let half = Math.floor(this.spacing / 2) + 0.5;
|
||||||
let start = half - this.length / 2;
|
let start = half - this.length / 2;
|
||||||
@@ -121,11 +121,11 @@ export class Grid {
|
|||||||
line: SVGElement, width: number, x1: number, x2: number, y1: number,
|
line: SVGElement, width: number, x1: number, x2: number, y1: number,
|
||||||
y2: number) {
|
y2: number) {
|
||||||
if (line) {
|
if (line) {
|
||||||
line.setAttribute('stroke-width', width.toString());
|
line.setAttribute('stroke-width', `${width}`);
|
||||||
line.setAttribute('x1', x1.toString());
|
line.setAttribute('x1', `${x1}`);
|
||||||
line.setAttribute('y1', y1.toString());
|
line.setAttribute('y1', `${y1}`);
|
||||||
line.setAttribute('x2', x2.toString());
|
line.setAttribute('x2', `${x2}`);
|
||||||
line.setAttribute('y2', y2.toString());
|
line.setAttribute('y2', `${y2}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +138,8 @@ export class Grid {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
moveTo(x: number, y: number) {
|
moveTo(x: number, y: number) {
|
||||||
this.pattern.setAttribute('x', x.toString());
|
this.pattern.setAttribute('x', `${x}`);
|
||||||
this.pattern.setAttribute('y', y.toString());
|
this.pattern.setAttribute('y', `${y}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ export class ASTNode {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
'The current AST location is not associated with a block');
|
'The current AST location is not associated with a block');
|
||||||
}
|
}
|
||||||
const curIdx = block.inputList.indexOf((input));
|
const curIdx = block.inputList.indexOf(input);
|
||||||
let fieldIdx = input.fieldRow.indexOf(location) + 1;
|
let fieldIdx = input.fieldRow.indexOf(location) + 1;
|
||||||
for (let i = curIdx; i < block.inputList.length; i++) {
|
for (let i = curIdx; i < block.inputList.length; i++) {
|
||||||
const newInput = block.inputList[i];
|
const newInput = block.inputList[i];
|
||||||
@@ -240,7 +240,7 @@ export class ASTNode {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
'The current AST location is not associated with a block');
|
'The current AST location is not associated with a block');
|
||||||
}
|
}
|
||||||
const curIdx = block.inputList.indexOf((parentInput));
|
const curIdx = block.inputList.indexOf(parentInput);
|
||||||
let fieldIdx = parentInput.fieldRow.indexOf(location) - 1;
|
let fieldIdx = parentInput.fieldRow.indexOf(location) - 1;
|
||||||
for (let i = curIdx; i >= 0; i--) {
|
for (let i = curIdx; i >= 0; i--) {
|
||||||
const input = block.inputList[i];
|
const input = block.inputList[i];
|
||||||
|
|||||||
19
core/menu.ts
19
core/menu.ts
@@ -17,7 +17,6 @@ import type {MenuItem} from './menuitem.js';
|
|||||||
import * as aria from './utils/aria.js';
|
import * as aria from './utils/aria.js';
|
||||||
import {Coordinate} from './utils/coordinate.js';
|
import {Coordinate} from './utils/coordinate.js';
|
||||||
import * as dom from './utils/dom.js';
|
import * as dom from './utils/dom.js';
|
||||||
import {KeyCodes} from './utils/keycodes.js';
|
|
||||||
import type {Size} from './utils/size.js';
|
import type {Size} from './utils/size.js';
|
||||||
import * as style from './utils/style.js';
|
import * as style from './utils/style.js';
|
||||||
|
|
||||||
@@ -392,29 +391,29 @@ export class Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const highlighted = this.highlightedItem;
|
const highlighted = this.highlightedItem;
|
||||||
switch (keyboardEvent.keyCode) {
|
switch (keyboardEvent.key) {
|
||||||
case KeyCodes.ENTER:
|
case 'Enter':
|
||||||
case KeyCodes.SPACE:
|
case ' ':
|
||||||
if (highlighted) {
|
if (highlighted) {
|
||||||
highlighted.performAction();
|
highlighted.performAction();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyCodes.UP:
|
case 'ArrowUp':
|
||||||
this.highlightPrevious();
|
this.highlightPrevious();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyCodes.DOWN:
|
case 'ArrowDown':
|
||||||
this.highlightNext();
|
this.highlightNext();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyCodes.PAGE_UP:
|
case 'PageUp':
|
||||||
case KeyCodes.HOME:
|
case 'Home':
|
||||||
this.highlightFirst();
|
this.highlightFirst();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyCodes.PAGE_DOWN:
|
case 'PageDown':
|
||||||
case KeyCodes.END:
|
case 'End':
|
||||||
this.highlightLast();
|
this.highlightLast();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -115,12 +115,12 @@ export function register<T>(
|
|||||||
AnyDuringMigration,
|
AnyDuringMigration,
|
||||||
opt_allowOverrides?: boolean): void {
|
opt_allowOverrides?: boolean): void {
|
||||||
if (!(type instanceof Type) && typeof type !== 'string' ||
|
if (!(type instanceof Type) && typeof type !== 'string' ||
|
||||||
String(type).trim() === '') {
|
`${type}`.trim() === '') {
|
||||||
throw Error(
|
throw Error(
|
||||||
'Invalid type "' + type + '". The type must be a' +
|
'Invalid type "' + type + '". The type must be a' +
|
||||||
' non-empty string or a Blockly.registry.Type.');
|
' non-empty string or a Blockly.registry.Type.');
|
||||||
}
|
}
|
||||||
type = String(type).toLowerCase();
|
type = `${type}`.toLowerCase();
|
||||||
|
|
||||||
if (typeof name !== 'string' || name.trim() === '') {
|
if (typeof name !== 'string' || name.trim() === '') {
|
||||||
throw Error(
|
throw Error(
|
||||||
@@ -178,7 +178,7 @@ function validate(type: string, registryItem: Function|AnyDuringMigration) {
|
|||||||
* @param name The plugin's name. (Ex. field_angle, geras)
|
* @param name The plugin's name. (Ex. field_angle, geras)
|
||||||
*/
|
*/
|
||||||
export function unregister<T>(type: string|Type<T>, name: string) {
|
export function unregister<T>(type: string|Type<T>, name: string) {
|
||||||
type = String(type).toLowerCase();
|
type = `${type}`.toLowerCase();
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
const typeRegistry = typeMap[type];
|
const typeRegistry = typeMap[type];
|
||||||
if (!typeRegistry || !typeRegistry[name]) {
|
if (!typeRegistry || !typeRegistry[name]) {
|
||||||
@@ -206,7 +206,7 @@ export function unregister<T>(type: string|Type<T>, name: string) {
|
|||||||
function getItem<T>(
|
function getItem<T>(
|
||||||
type: string|Type<T>, name: string, opt_throwIfMissing?: boolean):
|
type: string|Type<T>, name: string, opt_throwIfMissing?: boolean):
|
||||||
(new (...p1: AnyDuringMigration[]) => T)|null|AnyDuringMigration {
|
(new (...p1: AnyDuringMigration[]) => T)|null|AnyDuringMigration {
|
||||||
type = String(type).toLowerCase();
|
type = `${type}`.toLowerCase();
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
const typeRegistry = typeMap[type];
|
const typeRegistry = typeMap[type];
|
||||||
if (!typeRegistry || !typeRegistry[name]) {
|
if (!typeRegistry || !typeRegistry[name]) {
|
||||||
@@ -233,7 +233,7 @@ function getItem<T>(
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
export function hasItem<T>(type: string|Type<T>, name: string): boolean {
|
export function hasItem<T>(type: string|Type<T>, name: string): boolean {
|
||||||
type = String(type).toLowerCase();
|
type = `${type}`.toLowerCase();
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
const typeRegistry = typeMap[type];
|
const typeRegistry = typeMap[type];
|
||||||
if (!typeRegistry) {
|
if (!typeRegistry) {
|
||||||
@@ -288,7 +288,7 @@ export function getObject<T>(
|
|||||||
export function getAllItems<T>(
|
export function getAllItems<T>(
|
||||||
type: string|Type<T>, opt_cased?: boolean, opt_throwIfMissing?: boolean):
|
type: string|Type<T>, opt_cased?: boolean, opt_throwIfMissing?: boolean):
|
||||||
{[key: string]: T|null|(new (...p1: AnyDuringMigration[]) => T)}|null {
|
{[key: string]: T|null|(new (...p1: AnyDuringMigration[]) => T)}|null {
|
||||||
type = String(type).toLowerCase();
|
type = `${type}`.toLowerCase();
|
||||||
const typeRegistry = typeMap[type];
|
const typeRegistry = typeMap[type];
|
||||||
if (!typeRegistry) {
|
if (!typeRegistry) {
|
||||||
const msg = `Unable to find [${type}] in the registry.`;
|
const msg = `Unable to find [${type}] in the registry.`;
|
||||||
@@ -304,9 +304,7 @@ export function getAllItems<T>(
|
|||||||
}
|
}
|
||||||
const nameRegistry = nameMap[type];
|
const nameRegistry = nameMap[type];
|
||||||
const casedRegistry = Object.create(null);
|
const casedRegistry = Object.create(null);
|
||||||
const keys = Object.keys(typeRegistry);
|
for (const key of Object.keys(typeRegistry)) {
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
const key = keys[i];
|
|
||||||
casedRegistry[nameRegistry[key]] = typeRegistry[key];
|
casedRegistry[nameRegistry[key]] = typeRegistry[key];
|
||||||
}
|
}
|
||||||
return casedRegistry;
|
return casedRegistry;
|
||||||
@@ -325,8 +323,7 @@ export function getAllItems<T>(
|
|||||||
export function getClassFromOptions<T>(
|
export function getClassFromOptions<T>(
|
||||||
type: Type<T>, options: Options, opt_throwIfMissing?: boolean):
|
type: Type<T>, options: Options, opt_throwIfMissing?: boolean):
|
||||||
(new (...p1: AnyDuringMigration[]) => T)|null {
|
(new (...p1: AnyDuringMigration[]) => T)|null {
|
||||||
const typeName = type.toString();
|
const plugin = options.plugins[String(type)] || DEFAULT;
|
||||||
const plugin = options.plugins[typeName] || DEFAULT;
|
|
||||||
|
|
||||||
// If the user passed in a plugin class instead of a registered plugin name.
|
// If the user passed in a plugin class instead of a registered plugin name.
|
||||||
if (typeof plugin === 'function') {
|
if (typeof plugin === 'function') {
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ export class MarkerSvg extends BaseMarkerSvg {
|
|||||||
* @param y The y position of the circle.
|
* @param y The y position of the circle.
|
||||||
*/
|
*/
|
||||||
private positionCircle_(x: number, y: number) {
|
private positionCircle_(x: number, y: number) {
|
||||||
this.markerCircle_?.setAttribute('cx', x.toString());
|
this.markerCircle_?.setAttribute('cx', `${x}`);
|
||||||
this.markerCircle_?.setAttribute('cy', y.toString());
|
this.markerCircle_?.setAttribute('cy', `${y}`);
|
||||||
this.currentMarkerSvg = this.markerCircle_;
|
this.currentMarkerSvg = this.markerCircle_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,12 +223,12 @@ export class Scrollbar {
|
|||||||
this.svgBackground.setAttribute('height', String(scrollbarThickness));
|
this.svgBackground.setAttribute('height', String(scrollbarThickness));
|
||||||
this.outerSvg.setAttribute('height', String(scrollbarThickness));
|
this.outerSvg.setAttribute('height', String(scrollbarThickness));
|
||||||
this.svgHandle.setAttribute('height', String(scrollbarThickness - 5));
|
this.svgHandle.setAttribute('height', String(scrollbarThickness - 5));
|
||||||
this.svgHandle.setAttribute('y', String(2.5));
|
this.svgHandle.setAttribute('y', '2.5');
|
||||||
} else {
|
} else {
|
||||||
this.svgBackground.setAttribute('width', String(scrollbarThickness));
|
this.svgBackground.setAttribute('width', String(scrollbarThickness));
|
||||||
this.outerSvg.setAttribute('width', String(scrollbarThickness));
|
this.outerSvg.setAttribute('width', String(scrollbarThickness));
|
||||||
this.svgHandle.setAttribute('width', String(scrollbarThickness - 5));
|
this.svgHandle.setAttribute('width', String(scrollbarThickness - 5));
|
||||||
this.svgHandle.setAttribute('x', String(2.5));
|
this.svgHandle.setAttribute('x', '2.5');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export class ShortcutRegistry {
|
|||||||
addKeyMapping(
|
addKeyMapping(
|
||||||
keyCode: string|number|KeyCodes, shortcutName: string,
|
keyCode: string|number|KeyCodes, shortcutName: string,
|
||||||
opt_allowCollision?: boolean) {
|
opt_allowCollision?: boolean) {
|
||||||
keyCode = String(keyCode);
|
keyCode = `${keyCode}`;
|
||||||
const shortcutNames = this.keyMap.get(keyCode);
|
const shortcutNames = this.keyMap.get(keyCode);
|
||||||
if (shortcutNames && !opt_allowCollision) {
|
if (shortcutNames && !opt_allowCollision) {
|
||||||
throw new Error(`Shortcut named "${
|
throw new Error(`Shortcut named "${
|
||||||
@@ -280,7 +280,7 @@ export class ShortcutRegistry {
|
|||||||
if (serializedKey !== '' && e.keyCode) {
|
if (serializedKey !== '' && e.keyCode) {
|
||||||
serializedKey = serializedKey + '+' + e.keyCode;
|
serializedKey = serializedKey + '+' + e.keyCode;
|
||||||
} else if (e.keyCode) {
|
} else if (e.keyCode) {
|
||||||
serializedKey = e.keyCode.toString();
|
serializedKey = String(e.keyCode);
|
||||||
}
|
}
|
||||||
return serializedKey;
|
return serializedKey;
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ export class ShortcutRegistry {
|
|||||||
if (serializedKey !== '' && keyCode) {
|
if (serializedKey !== '' && keyCode) {
|
||||||
serializedKey = serializedKey + '+' + keyCode;
|
serializedKey = serializedKey + '+' + keyCode;
|
||||||
} else if (keyCode) {
|
} else if (keyCode) {
|
||||||
serializedKey = keyCode.toString();
|
serializedKey = `${keyCode}`;
|
||||||
}
|
}
|
||||||
return serializedKey;
|
return serializedKey;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,10 +116,16 @@ export class Theme implements ITheme {
|
|||||||
*/
|
*/
|
||||||
getComponentStyle(componentName: string): string|null {
|
getComponentStyle(componentName: string): string|null {
|
||||||
const style = (this.componentStyles as AnyDuringMigration)[componentName];
|
const style = (this.componentStyles as AnyDuringMigration)[componentName];
|
||||||
if (style && typeof style === 'string' && this.getComponentStyle((style))) {
|
if (!style) {
|
||||||
return this.getComponentStyle((style));
|
return null;
|
||||||
}
|
}
|
||||||
return style ? String(style) : null;
|
if (typeof style === 'string') {
|
||||||
|
const recursiveStyle = this.getComponentStyle(style);
|
||||||
|
if (recursiveStyle) {
|
||||||
|
return recursiveStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${style}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -57,19 +57,19 @@ export class ToolboxCategory extends ToolboxItem implements
|
|||||||
/** The colour of the category. */
|
/** The colour of the category. */
|
||||||
protected colour_ = '';
|
protected colour_ = '';
|
||||||
|
|
||||||
/** The html container for the category. */
|
/** The HTML container for the category. */
|
||||||
protected htmlDiv_: HTMLDivElement|null = null;
|
protected htmlDiv_: HTMLDivElement|null = null;
|
||||||
|
|
||||||
/** The html element for the category row. */
|
/** The HTML element for the category row. */
|
||||||
protected rowDiv_: HTMLDivElement|null = null;
|
protected rowDiv_: HTMLDivElement|null = null;
|
||||||
|
|
||||||
/** The html element that holds children elements of the category row. */
|
/** The HTML element that holds children elements of the category row. */
|
||||||
protected rowContents_: HTMLDivElement|null = null;
|
protected rowContents_: HTMLDivElement|null = null;
|
||||||
|
|
||||||
/** The html element for the toolbox icon. */
|
/** The HTML element for the toolbox icon. */
|
||||||
protected iconDom_: Element|null = null;
|
protected iconDom_: Element|null = null;
|
||||||
|
|
||||||
/** The html element for the toolbox label. */
|
/** The HTML element for the toolbox label. */
|
||||||
protected labelDom_: Element|null = null;
|
protected labelDom_: Element|null = null;
|
||||||
protected cssConfig_: CssConfig;
|
protected cssConfig_: CssConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import type {KeyboardShortcut} from '../shortcut_registry.js';
|
|||||||
import * as Touch from '../touch.js';
|
import * as Touch from '../touch.js';
|
||||||
import * as aria from '../utils/aria.js';
|
import * as aria from '../utils/aria.js';
|
||||||
import * as dom from '../utils/dom.js';
|
import * as dom from '../utils/dom.js';
|
||||||
import {KeyCodes} from '../utils/keycodes.js';
|
|
||||||
import {Rect} from '../utils/rect.js';
|
import {Rect} from '../utils/rect.js';
|
||||||
import * as toolbox from '../utils/toolbox.js';
|
import * as toolbox from '../utils/toolbox.js';
|
||||||
import type {WorkspaceSvg} from '../workspace_svg.js';
|
import type {WorkspaceSvg} from '../workspace_svg.js';
|
||||||
@@ -63,10 +62,10 @@ export class Toolbox extends DeleteArea implements IAutoHideable,
|
|||||||
protected toolboxDef_: toolbox.ToolboxInfo;
|
protected toolboxDef_: toolbox.ToolboxInfo;
|
||||||
private readonly horizontalLayout_: boolean;
|
private readonly horizontalLayout_: boolean;
|
||||||
|
|
||||||
/** The html container for the toolbox. */
|
/** The HTML container for the toolbox. */
|
||||||
HtmlDiv: HTMLDivElement|null = null;
|
HtmlDiv: HTMLDivElement|null = null;
|
||||||
|
|
||||||
/** The html container for the contents of a toolbox. */
|
/** The HTML container for the contents of a toolbox. */
|
||||||
protected contentsDiv_: HTMLDivElement|null = null;
|
protected contentsDiv_: HTMLDivElement|null = null;
|
||||||
|
|
||||||
/** Whether the Toolbox is visible. */
|
/** Whether the Toolbox is visible. */
|
||||||
@@ -268,21 +267,21 @@ export class Toolbox extends DeleteArea implements IAutoHideable,
|
|||||||
*/
|
*/
|
||||||
protected onKeyDown_(e: KeyboardEvent) {
|
protected onKeyDown_(e: KeyboardEvent) {
|
||||||
let handled = false;
|
let handled = false;
|
||||||
switch (e.keyCode) {
|
switch (e.key) {
|
||||||
case KeyCodes.DOWN:
|
case 'ArrowDown':
|
||||||
handled = this.selectNext_();
|
handled = this.selectNext_();
|
||||||
break;
|
break;
|
||||||
case KeyCodes.UP:
|
case 'ArrowUp':
|
||||||
handled = this.selectPrevious_();
|
handled = this.selectPrevious_();
|
||||||
break;
|
break;
|
||||||
case KeyCodes.LEFT:
|
case 'ArrowLeft':
|
||||||
handled = this.selectParent_();
|
handled = this.selectParent_();
|
||||||
break;
|
break;
|
||||||
case KeyCodes.RIGHT:
|
case 'ArrowRight':
|
||||||
handled = this.selectChild_();
|
handled = this.selectChild_();
|
||||||
break;
|
break;
|
||||||
case KeyCodes.ENTER:
|
case 'Enter':
|
||||||
case KeyCodes.SPACE:
|
case ' ':
|
||||||
if (this.selectedItem_ && this.selectedItem_.isCollapsible()) {
|
if (this.selectedItem_ && this.selectedItem_.isCollapsible()) {
|
||||||
const collapsibleItem = this.selectedItem_ as ICollapsibleToolboxItem;
|
const collapsibleItem = this.selectedItem_ as ICollapsibleToolboxItem;
|
||||||
collapsibleItem.toggleExpanded();
|
collapsibleItem.toggleExpanded();
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ export class Trashcan extends DeleteArea implements IAutoHideable,
|
|||||||
// Linear interpolation between min and max.
|
// Linear interpolation between min and max.
|
||||||
const opacity = OPACITY_MIN + this.lidOpen_ * (OPACITY_MAX - OPACITY_MIN);
|
const opacity = OPACITY_MIN + this.lidOpen_ * (OPACITY_MAX - OPACITY_MIN);
|
||||||
if (this.svgGroup_) {
|
if (this.svgGroup_) {
|
||||||
this.svgGroup_.style.opacity = opacity.toString();
|
this.svgGroup_.style.opacity = `${opacity}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lidOpen_ > this.minOpenness_ && this.lidOpen_ < 1) {
|
if (this.lidOpen_ > this.minOpenness_ && this.lidOpen_ < 1) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export function setHsvValue(newValue: number) {
|
|||||||
* can't be parsed.
|
* can't be parsed.
|
||||||
*/
|
*/
|
||||||
export function parse(str: string|number): string|null {
|
export function parse(str: string|number): string|null {
|
||||||
str = String(str).toLowerCase().trim();
|
str = `${str}`.toLowerCase().trim();
|
||||||
let hex = names[str];
|
let hex = names[str];
|
||||||
if (hex) {
|
if (hex) {
|
||||||
// e.g. 'red'
|
// e.g. 'red'
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ let canvasContext: CanvasRenderingContext2D|null = null;
|
|||||||
export function createSvgElement<T extends SVGElement>(
|
export function createSvgElement<T extends SVGElement>(
|
||||||
name: string|Svg<T>, attrs: {[key: string]: string|number},
|
name: string|Svg<T>, attrs: {[key: string]: string|number},
|
||||||
opt_parent?: Element|null): T {
|
opt_parent?: Element|null): T {
|
||||||
const e = document.createElementNS(SVG_NS, String(name)) as T;
|
const e = document.createElementNS(SVG_NS, `${name}`) as T;
|
||||||
for (const key in attrs) {
|
for (const key in attrs) {
|
||||||
e.setAttribute(key, `${attrs[key]}`);
|
e.setAttribute(key, `${attrs[key]}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ function tokenizeInterpolationInternal(
|
|||||||
// When parsing interpolation tokens, numbers are special
|
// When parsing interpolation tokens, numbers are special
|
||||||
// placeholders (%1, %2, etc). Make sure all other values are
|
// placeholders (%1, %2, etc). Make sure all other values are
|
||||||
// strings.
|
// strings.
|
||||||
tokens.push(String(rawValue));
|
tokens.push(`${rawValue}`);
|
||||||
} else {
|
} else {
|
||||||
tokens.push(rawValue);
|
tokens.push(rawValue);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,10 +265,10 @@ export class WorkspaceComment {
|
|||||||
*/
|
*/
|
||||||
toXmlWithXY(opt_noId?: boolean): Element {
|
toXmlWithXY(opt_noId?: boolean): Element {
|
||||||
const element = this.toXml(opt_noId);
|
const element = this.toXml(opt_noId);
|
||||||
element.setAttribute('x', `${Math.round(this.xy_.x)}`);
|
element.setAttribute('x', String(Math.round(this.xy_.x)));
|
||||||
element.setAttribute('y', `${Math.round(this.xy_.y)}`);
|
element.setAttribute('y', String(Math.round(this.xy_.y)));
|
||||||
element.setAttribute('h', `${this.height_}`);
|
element.setAttribute('h', String(this.height_));
|
||||||
element.setAttribute('w', `${this.width_}`);
|
element.setAttribute('w', String(this.width_));
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -723,8 +723,10 @@ export class WorkspaceCommentSvg extends WorkspaceComment implements
|
|||||||
Svg.G, {'class': this.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'},
|
Svg.G, {'class': this.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'},
|
||||||
this.svgGroup_);
|
this.svgGroup_);
|
||||||
dom.createSvgElement(
|
dom.createSvgElement(
|
||||||
Svg.POLYGON,
|
Svg.POLYGON, {
|
||||||
{'points': '0,x x,x x,0'.replace(/x/g, RESIZE_SIZE.toString())},
|
'points':
|
||||||
|
`0,${RESIZE_SIZE} ${RESIZE_SIZE},${RESIZE_SIZE} ${RESIZE_SIZE},0`,
|
||||||
|
},
|
||||||
this.resizeGroup_);
|
this.resizeGroup_);
|
||||||
dom.createSvgElement(
|
dom.createSvgElement(
|
||||||
Svg.LINE, {
|
Svg.LINE, {
|
||||||
@@ -885,10 +887,11 @@ export class WorkspaceCommentSvg extends WorkspaceComment implements
|
|||||||
const topOffset = WorkspaceCommentSvg.TOP_OFFSET;
|
const topOffset = WorkspaceCommentSvg.TOP_OFFSET;
|
||||||
const textOffset = TEXTAREA_OFFSET * 2;
|
const textOffset = TEXTAREA_OFFSET * 2;
|
||||||
|
|
||||||
this.foreignObject_?.setAttribute('width', `${size.width}`);
|
this.foreignObject_?.setAttribute('width', String(size.width));
|
||||||
this.foreignObject_?.setAttribute('height', `${size.height - topOffset}`);
|
this.foreignObject_?.setAttribute(
|
||||||
|
'height', String(size.height - topOffset));
|
||||||
if (this.RTL) {
|
if (this.RTL) {
|
||||||
this.foreignObject_?.setAttribute('x', `${- size.width}`);
|
this.foreignObject_?.setAttribute('x', String(-size.width));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.textarea_) return;
|
if (!this.textarea_) return;
|
||||||
@@ -914,7 +917,7 @@ export class WorkspaceCommentSvg extends WorkspaceComment implements
|
|||||||
this.svgRectTarget_?.setAttribute('height', `${height}`);
|
this.svgRectTarget_?.setAttribute('height', `${height}`);
|
||||||
this.svgHandleTarget_?.setAttribute('width', `${width}`);
|
this.svgHandleTarget_?.setAttribute('width', `${width}`);
|
||||||
this.svgHandleTarget_?.setAttribute(
|
this.svgHandleTarget_?.setAttribute(
|
||||||
'height', `${WorkspaceCommentSvg.TOP_OFFSET}`);
|
'height', String(WorkspaceCommentSvg.TOP_OFFSET));
|
||||||
if (this.RTL) {
|
if (this.RTL) {
|
||||||
this.svgRect_.setAttribute('transform', 'scale(-1 1)');
|
this.svgRect_.setAttribute('transform', 'scale(-1 1)');
|
||||||
this.svgRectTarget_?.setAttribute('transform', 'scale(-1 1)');
|
this.svgRectTarget_?.setAttribute('transform', 'scale(-1 1)');
|
||||||
@@ -1042,7 +1045,7 @@ export class WorkspaceCommentSvg extends WorkspaceComment implements
|
|||||||
eventUtils.enable();
|
eventUtils.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkspaceComment.fireCreateEvent((comment));
|
WorkspaceComment.fireCreateEvent(comment);
|
||||||
return comment;
|
return comment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1074,13 +1074,13 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg {
|
|||||||
this.cachedParentSvgSize.width = width;
|
this.cachedParentSvgSize.width = width;
|
||||||
// This is set to support the public (but deprecated) Blockly.svgSize
|
// This is set to support the public (but deprecated) Blockly.svgSize
|
||||||
// method.
|
// method.
|
||||||
svg.setAttribute('data-cached-width', width.toString());
|
svg.setAttribute('data-cached-width', `${width}`);
|
||||||
}
|
}
|
||||||
if (height != null) {
|
if (height != null) {
|
||||||
this.cachedParentSvgSize.height = height;
|
this.cachedParentSvgSize.height = height;
|
||||||
// This is set to support the public (but deprecated) Blockly.svgSize
|
// This is set to support the public (but deprecated) Blockly.svgSize
|
||||||
// method.
|
// method.
|
||||||
svg.setAttribute('data-cached-height', height.toString());
|
svg.setAttribute('data-cached-height', `${height}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
core/xml.ts
10
core/xml.ts
@@ -106,8 +106,8 @@ export function blockToDomWithXY(block: Block, opt_noId?: boolean): Element|
|
|||||||
if (isElement(element)) {
|
if (isElement(element)) {
|
||||||
const xy = block.getRelativeToSurfaceXY();
|
const xy = block.getRelativeToSurfaceXY();
|
||||||
element.setAttribute(
|
element.setAttribute(
|
||||||
'x', `${Math.round(block.workspace.RTL ? width - xy.x : xy.x)}`);
|
'x', String(Math.round(block.workspace.RTL ? width - xy.x : xy.x)));
|
||||||
element.setAttribute('y', `${Math.round(xy.y)}`);
|
element.setAttribute('y', String(Math.round(xy.y)));
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
@@ -191,8 +191,8 @@ export function blockToDom(block: Block, opt_noId?: boolean): Element|
|
|||||||
const commentElement = utilsXml.createElement('comment');
|
const commentElement = utilsXml.createElement('comment');
|
||||||
commentElement.appendChild(utilsXml.createTextNode(commentText));
|
commentElement.appendChild(utilsXml.createTextNode(commentText));
|
||||||
commentElement.setAttribute('pinned', `${pinned}`);
|
commentElement.setAttribute('pinned', `${pinned}`);
|
||||||
commentElement.setAttribute('h', `${size.height}`);
|
commentElement.setAttribute('h', String(size.height));
|
||||||
commentElement.setAttribute('w', `${size.width}`);
|
commentElement.setAttribute('w', String(size.width));
|
||||||
|
|
||||||
element.appendChild(commentElement);
|
element.appendChild(commentElement);
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,7 @@ export function blockToDom(block: Block, opt_noId?: boolean): Element|
|
|||||||
}
|
}
|
||||||
if (block.inputsInline !== undefined &&
|
if (block.inputsInline !== undefined &&
|
||||||
block.inputsInline !== block.inputsInlineDefault) {
|
block.inputsInline !== block.inputsInlineDefault) {
|
||||||
element.setAttribute('inline', block.inputsInline.toString());
|
element.setAttribute('inline', String(block.inputsInline));
|
||||||
}
|
}
|
||||||
if (block.isCollapsed()) {
|
if (block.isCollapsed()) {
|
||||||
element.setAttribute('collapsed', 'true');
|
element.setAttribute('collapsed', 'true');
|
||||||
|
|||||||
@@ -42,22 +42,11 @@ export class ZoomControls implements IPositionable {
|
|||||||
id = 'zoomControls';
|
id = 'zoomControls';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A handle to use to unbind the mouse down event handler for zoom reset
|
* Array holding info needed to unbind events.
|
||||||
* button. Opaque data returned from browserEvents.conditionalBind.
|
* Used for disposing.
|
||||||
|
* Ex: [[node, name, func], [node, name, func]].
|
||||||
*/
|
*/
|
||||||
private onZoomResetWrapper: browserEvents.Data|null = null;
|
private boundEvents: browserEvents.Data[] = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* A handle to use to unbind the mouse down event handler for zoom in
|
|
||||||
* button. Opaque data returned from browserEvents.conditionalBind.
|
|
||||||
*/
|
|
||||||
private onZoomInWrapper: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A handle to use to unbind the mouse down event handler for zoom out
|
|
||||||
* button. Opaque data returned from browserEvents.conditionalBind.
|
|
||||||
*/
|
|
||||||
private onZoomOutWrapper: browserEvents.Data|null = null;
|
|
||||||
|
|
||||||
/** The zoom in svg <g> element. */
|
/** The zoom in svg <g> element. */
|
||||||
private zoomInGroup: SVGGElement|null = null;
|
private zoomInGroup: SVGGElement|null = null;
|
||||||
@@ -144,15 +133,10 @@ export class ZoomControls implements IPositionable {
|
|||||||
if (this.svgGroup) {
|
if (this.svgGroup) {
|
||||||
dom.removeNode(this.svgGroup);
|
dom.removeNode(this.svgGroup);
|
||||||
}
|
}
|
||||||
if (this.onZoomResetWrapper) {
|
for (const event of this.boundEvents) {
|
||||||
browserEvents.unbind(this.onZoomResetWrapper);
|
browserEvents.unbind(event);
|
||||||
}
|
|
||||||
if (this.onZoomInWrapper) {
|
|
||||||
browserEvents.unbind(this.onZoomInWrapper);
|
|
||||||
}
|
|
||||||
if (this.onZoomOutWrapper) {
|
|
||||||
browserEvents.unbind(this.onZoomOutWrapper);
|
|
||||||
}
|
}
|
||||||
|
this.boundEvents.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,8 +257,8 @@ export class ZoomControls implements IPositionable {
|
|||||||
this.workspace.options.pathToMedia + SPRITE.url);
|
this.workspace.options.pathToMedia + SPRITE.url);
|
||||||
|
|
||||||
// Attach listener.
|
// Attach listener.
|
||||||
this.onZoomOutWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
this.zoomOutGroup, 'pointerdown', null, this.zoom.bind(this, -1));
|
this.zoomOutGroup, 'pointerdown', null, this.zoom.bind(this, -1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,8 +303,8 @@ export class ZoomControls implements IPositionable {
|
|||||||
this.workspace.options.pathToMedia + SPRITE.url);
|
this.workspace.options.pathToMedia + SPRITE.url);
|
||||||
|
|
||||||
// Attach listener.
|
// Attach listener.
|
||||||
this.onZoomInWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
this.zoomInGroup, 'pointerdown', null, this.zoom.bind(this, 1));
|
this.zoomInGroup, 'pointerdown', null, this.zoom.bind(this, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -377,8 +361,8 @@ export class ZoomControls implements IPositionable {
|
|||||||
this.workspace.options.pathToMedia + SPRITE.url);
|
this.workspace.options.pathToMedia + SPRITE.url);
|
||||||
|
|
||||||
// Attach event listeners.
|
// Attach event listeners.
|
||||||
this.onZoomResetWrapper = browserEvents.conditionalBind(
|
this.boundEvents.push(browserEvents.conditionalBind(
|
||||||
this.zoomResetGroup, 'pointerdown', null, this.resetZoom.bind(this));
|
this.zoomResetGroup, 'pointerdown', null, this.resetZoom.bind(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -171,8 +171,7 @@ BlocklyDevTools.Analytics.onExport = function(typeId, optMetadata) {
|
|||||||
*/
|
*/
|
||||||
BlocklyDevTools.Analytics.onError = function(e) {
|
BlocklyDevTools.Analytics.onError = function(e) {
|
||||||
// stub
|
// stub
|
||||||
this.LOG_TO_CONSOLE_ &&
|
this.LOG_TO_CONSOLE_ && console.log('Analytics.onError("' + e + '")');
|
||||||
console.log('Analytics.onError("' + e.toString() + '")');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -356,9 +356,9 @@ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(type, direction) {
|
|||||||
'NUMERIC': function(a, b) {
|
'NUMERIC': function(a, b) {
|
||||||
return Number(a) - Number(b); },
|
return Number(a) - Number(b); },
|
||||||
'TEXT': function(a, b) {
|
'TEXT': function(a, b) {
|
||||||
return a.toString() > b.toString() ? 1 : -1; },
|
return String(a) > String(b) ? 1 : -1; },
|
||||||
'IGNORE_CASE': function(a, b) {
|
'IGNORE_CASE': function(a, b) {
|
||||||
return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; },
|
return String(a).toLowerCase() > String(b).toLowerCase() ? 1 : -1; },
|
||||||
};
|
};
|
||||||
var compare = compareFuncs[type];
|
var compare = compareFuncs[type];
|
||||||
return function(a, b) { return compare(a, b) * direction; };
|
return function(a, b) { return compare(a, b) * direction; };
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function runTestTask(id, task) {
|
|||||||
successCount++;
|
successCount++;
|
||||||
if (process.env.CI) console.log('::endgroup::');
|
if (process.env.CI) console.log('::endgroup::');
|
||||||
console.log(`${BOLD_GREEN}SUCCESS:${ANSI_RESET} ${id}`);
|
console.log(`${BOLD_GREEN}SUCCESS:${ANSI_RESET} ${id}`);
|
||||||
results[id] = {success: true};
|
results[id] = {success: true};
|
||||||
resolve(result);
|
resolve(result);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -191,8 +191,8 @@ function compareSize(file, expected) {
|
|||||||
const message = `Failed: Previous size of ${name} is undefined.`;
|
const message = `Failed: Previous size of ${name} is undefined.`;
|
||||||
console.log(`${BOLD_RED}${message}${ANSI_RESET}`);
|
console.log(`${BOLD_RED}${message}${ANSI_RESET}`);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > compare) {
|
if (size > compare) {
|
||||||
const message = `Failed: ` +
|
const message = `Failed: ` +
|
||||||
`Size of ${name} has grown more than 10%. ${size} vs ${expected}`;
|
`Size of ${name} has grown more than 10%. ${size} vs ${expected}`;
|
||||||
|
|||||||
@@ -1338,9 +1338,9 @@ function listsGetSortCompare(type, direction) {
|
|||||||
'NUMERIC': function(a, b) {
|
'NUMERIC': function(a, b) {
|
||||||
return Number(a) - Number(b); },
|
return Number(a) - Number(b); },
|
||||||
'TEXT': function(a, b) {
|
'TEXT': function(a, b) {
|
||||||
return a.toString() > b.toString() ? 1 : -1; },
|
return String(a) > String(b) ? 1 : -1; },
|
||||||
'IGNORE_CASE': function(a, b) {
|
'IGNORE_CASE': function(a, b) {
|
||||||
return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; },
|
return String(a).toLowerCase() > String(b).toLowerCase() ? 1 : -1; },
|
||||||
};
|
};
|
||||||
var compare = compareFuncs[type];
|
var compare = compareFuncs[type];
|
||||||
return function(a, b) { return compare(a, b) * direction; };
|
return function(a, b) { return compare(a, b) * direction; };
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ suite('Blocks', function() {
|
|||||||
setup(function() {
|
setup(function() {
|
||||||
this.blocks = createTestBlocks(this.workspace, true);
|
this.blocks = createTestBlocks(this.workspace, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Don\'t heal', function() {
|
test('Don\'t heal', function() {
|
||||||
this.blocks.B.dispose(false);
|
this.blocks.B.dispose(false);
|
||||||
assertDisposedNoheal(this.blocks);
|
assertDisposedNoheal(this.blocks);
|
||||||
@@ -313,11 +313,11 @@ suite('Blocks', function() {
|
|||||||
|
|
||||||
test('Heal with bad checks', function() {
|
test('Heal with bad checks', function() {
|
||||||
const blocks = this.blocks;
|
const blocks = this.blocks;
|
||||||
|
|
||||||
// A and C can't connect, but both can connect to B.
|
// A and C can't connect, but both can connect to B.
|
||||||
blocks.A.inputList[0].connection.setCheck('type1');
|
blocks.A.inputList[0].connection.setCheck('type1');
|
||||||
blocks.C.outputConnection.setCheck('type2');
|
blocks.C.outputConnection.setCheck('type2');
|
||||||
|
|
||||||
// Each block has only one input, but the types don't work.
|
// Each block has only one input, but the types don't work.
|
||||||
blocks.B.dispose(true);
|
blocks.B.dispose(true);
|
||||||
assertDisposedHealFailed(blocks);
|
assertDisposedHealFailed(blocks);
|
||||||
@@ -362,7 +362,7 @@ suite('Blocks', function() {
|
|||||||
setup(function() {
|
setup(function() {
|
||||||
this.blocks = createTestBlocks(this.workspace, false);
|
this.blocks = createTestBlocks(this.workspace, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Don\'t heal', function() {
|
test('Don\'t heal', function() {
|
||||||
this.blocks.B.dispose();
|
this.blocks.B.dispose();
|
||||||
assertDisposedNoheal(this.blocks);
|
assertDisposedNoheal(this.blocks);
|
||||||
@@ -378,10 +378,10 @@ suite('Blocks', function() {
|
|||||||
// A and C can't connect, but both can connect to B.
|
// A and C can't connect, but both can connect to B.
|
||||||
blocks.A.nextConnection.setCheck('type1');
|
blocks.A.nextConnection.setCheck('type1');
|
||||||
blocks.C.previousConnection.setCheck('type2');
|
blocks.C.previousConnection.setCheck('type2');
|
||||||
|
|
||||||
// The types don't work.
|
// The types don't work.
|
||||||
blocks.B.dispose(true);
|
blocks.B.dispose(true);
|
||||||
|
|
||||||
assertDisposedHealFailed(blocks);
|
assertDisposedHealFailed(blocks);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ suite('Procedures', function() {
|
|||||||
createProcDefBlock(this.workspace, undefined, undefined, 'procB');
|
createProcDefBlock(this.workspace, undefined, undefined, 'procB');
|
||||||
const callBlockB =
|
const callBlockB =
|
||||||
createProcCallBlock(this.workspace, undefined, 'procB');
|
createProcCallBlock(this.workspace, undefined, 'procB');
|
||||||
|
|
||||||
defBlockB.setFieldValue('procA', 'NAME');
|
defBlockB.setFieldValue('procA', 'NAME');
|
||||||
|
|
||||||
chai.assert.notEqual(
|
chai.assert.notEqual(
|
||||||
defBlockB.getFieldValue('NAME'),
|
defBlockB.getFieldValue('NAME'),
|
||||||
'procA',
|
'procA',
|
||||||
@@ -82,7 +82,7 @@ suite('Procedures', function() {
|
|||||||
},
|
},
|
||||||
mutatorWorkspace);
|
mutatorWorkspace);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
const newFlyoutParamName =
|
const newFlyoutParamName =
|
||||||
mutatorWorkspace.getFlyout().getWorkspace().getTopBlocks(true)[0]
|
mutatorWorkspace.getFlyout().getWorkspace().getTopBlocks(true)[0]
|
||||||
.getFieldValue('NAME');
|
.getFieldValue('NAME');
|
||||||
@@ -167,7 +167,7 @@ suite('Procedures', function() {
|
|||||||
|
|
||||||
this.workspace.undo();
|
this.workspace.undo();
|
||||||
this.workspace.undo(/* redo= */ true);
|
this.workspace.undo(/* redo= */ true);
|
||||||
|
|
||||||
chai.assert.isNotNull(
|
chai.assert.isNotNull(
|
||||||
defBlock.getField('PARAMS'),
|
defBlock.getField('PARAMS'),
|
||||||
'Expected the params field to exist');
|
'Expected the params field to exist');
|
||||||
@@ -252,10 +252,10 @@ suite('Procedures', function() {
|
|||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
paramBlock.checkAndDelete();
|
paramBlock.checkAndDelete();
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
this.workspace.undo();
|
this.workspace.undo();
|
||||||
this.workspace.undo(/* redo= */ true);
|
this.workspace.undo(/* redo= */ true);
|
||||||
|
|
||||||
chai.assert.isFalse(
|
chai.assert.isFalse(
|
||||||
defBlock.getFieldValue('PARAMS').includes('param1'),
|
defBlock.getFieldValue('PARAMS').includes('param1'),
|
||||||
'Expected the params field to not contain the name of the new param');
|
'Expected the params field to not contain the name of the new param');
|
||||||
@@ -300,10 +300,10 @@ suite('Procedures', function() {
|
|||||||
.connect(paramBlock1.previousConnection);
|
.connect(paramBlock1.previousConnection);
|
||||||
paramBlock1.nextConnection.connect(paramBlock2.previousConnection);
|
paramBlock1.nextConnection.connect(paramBlock2.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
paramBlock1.setFieldValue('new name', 'NAME');
|
paramBlock1.setFieldValue('new name', 'NAME');
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
chai.assert.isNotNull(
|
chai.assert.isNotNull(
|
||||||
defBlock.getField('PARAMS'),
|
defBlock.getField('PARAMS'),
|
||||||
'Expected the params field to exist');
|
'Expected the params field to exist');
|
||||||
@@ -349,10 +349,10 @@ suite('Procedures', function() {
|
|||||||
paramBlock.setFieldValue('param1', 'NAME');
|
paramBlock.setFieldValue('param1', 'NAME');
|
||||||
containerBlock.getInput('STACK').connection.connect(paramBlock.previousConnection);
|
containerBlock.getInput('STACK').connection.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
paramBlock.setFieldValue('param2', 'NAME');
|
paramBlock.setFieldValue('param2', 'NAME');
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
chai.assert.isNotNull(
|
chai.assert.isNotNull(
|
||||||
this.workspace.getVariable('param1', ''),
|
this.workspace.getVariable('param1', ''),
|
||||||
'Expected the old variable to continue to exist');
|
'Expected the old variable to continue to exist');
|
||||||
@@ -372,10 +372,10 @@ suite('Procedures', function() {
|
|||||||
.connect(paramBlock.previousConnection);
|
.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
defBlock.mutator.setVisible(false);
|
defBlock.mutator.setVisible(false);
|
||||||
|
|
||||||
const variable = this.workspace.getVariable('param1', '');
|
const variable = this.workspace.getVariable('param1', '');
|
||||||
this.workspace.renameVariableById(variable.getId(), 'new name');
|
this.workspace.renameVariableById(variable.getId(), 'new name');
|
||||||
|
|
||||||
chai.assert.isNotNull(
|
chai.assert.isNotNull(
|
||||||
defBlock.getField('PARAMS'),
|
defBlock.getField('PARAMS'),
|
||||||
'Expected the params field to exist');
|
'Expected the params field to exist');
|
||||||
@@ -397,10 +397,10 @@ suite('Procedures', function() {
|
|||||||
containerBlock.getInput('STACK').connection
|
containerBlock.getInput('STACK').connection
|
||||||
.connect(paramBlock.previousConnection);
|
.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
const variable = this.workspace.getVariable('param1', '');
|
const variable = this.workspace.getVariable('param1', '');
|
||||||
this.workspace.renameVariableById(variable.getId(), 'new name');
|
this.workspace.renameVariableById(variable.getId(), 'new name');
|
||||||
|
|
||||||
chai.assert.equal(
|
chai.assert.equal(
|
||||||
paramBlock.getFieldValue('NAME'),
|
paramBlock.getFieldValue('NAME'),
|
||||||
'new name',
|
'new name',
|
||||||
@@ -422,7 +422,7 @@ suite('Procedures', function() {
|
|||||||
.connect(paramBlock.previousConnection);
|
.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
defBlock.mutator.setVisible(false);
|
defBlock.mutator.setVisible(false);
|
||||||
|
|
||||||
const variable = this.workspace.getVariable('param1', '');
|
const variable = this.workspace.getVariable('param1', '');
|
||||||
this.workspace.renameVariableById(variable.getId(), 'new name');
|
this.workspace.renameVariableById(variable.getId(), 'new name');
|
||||||
|
|
||||||
@@ -449,10 +449,10 @@ suite('Procedures', function() {
|
|||||||
.connect(paramBlock.previousConnection);
|
.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
defBlock.mutator.setVisible(false);
|
defBlock.mutator.setVisible(false);
|
||||||
|
|
||||||
const variable = this.workspace.getVariable('param1', '');
|
const variable = this.workspace.getVariable('param1', '');
|
||||||
this.workspace.renameVariableById(variable.getId(), 'preCreatedVar');
|
this.workspace.renameVariableById(variable.getId(), 'preCreatedVar');
|
||||||
|
|
||||||
chai.assert.isNotNull(
|
chai.assert.isNotNull(
|
||||||
defBlock.getField('PARAMS'),
|
defBlock.getField('PARAMS'),
|
||||||
'Expected the params field to exist');
|
'Expected the params field to exist');
|
||||||
@@ -474,10 +474,10 @@ suite('Procedures', function() {
|
|||||||
containerBlock.getInput('STACK').connection
|
containerBlock.getInput('STACK').connection
|
||||||
.connect(paramBlock.previousConnection);
|
.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
const variable = this.workspace.getVariable('param1', '');
|
const variable = this.workspace.getVariable('param1', '');
|
||||||
this.workspace.renameVariableById(variable.getId(), 'preCreatedVar');
|
this.workspace.renameVariableById(variable.getId(), 'preCreatedVar');
|
||||||
|
|
||||||
chai.assert.equal(
|
chai.assert.equal(
|
||||||
paramBlock.getFieldValue('NAME'),
|
paramBlock.getFieldValue('NAME'),
|
||||||
'preCreatedVar',
|
'preCreatedVar',
|
||||||
@@ -499,7 +499,7 @@ suite('Procedures', function() {
|
|||||||
.connect(paramBlock.previousConnection);
|
.connect(paramBlock.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
defBlock.mutator.setVisible(false);
|
defBlock.mutator.setVisible(false);
|
||||||
|
|
||||||
const variable = this.workspace.getVariable('param1', '');
|
const variable = this.workspace.getVariable('param1', '');
|
||||||
this.workspace.renameVariableById(variable.getId(), 'preCreatedVar');
|
this.workspace.renameVariableById(variable.getId(), 'preCreatedVar');
|
||||||
|
|
||||||
@@ -571,7 +571,7 @@ suite('Procedures', function() {
|
|||||||
|
|
||||||
this.workspace.undo();
|
this.workspace.undo();
|
||||||
this.workspace.undo(/* redo= */ true);
|
this.workspace.undo(/* redo= */ true);
|
||||||
|
|
||||||
chai.assert.isTrue(
|
chai.assert.isTrue(
|
||||||
defBlock.getFieldValue('PARAMS').includes('new'),
|
defBlock.getFieldValue('PARAMS').includes('new'),
|
||||||
'Expected the params field to contain the new name of the param');
|
'Expected the params field to contain the new name of the param');
|
||||||
@@ -671,14 +671,14 @@ suite('Procedures', function() {
|
|||||||
.connect(block1.outputConnection);
|
.connect(block1.outputConnection);
|
||||||
callBlock.getInput('ARG1').connection
|
callBlock.getInput('ARG1').connection
|
||||||
.connect(block2.outputConnection);
|
.connect(block2.outputConnection);
|
||||||
|
|
||||||
// Reorder the parameters.
|
// Reorder the parameters.
|
||||||
paramBlock2.previousConnection.disconnect();
|
paramBlock2.previousConnection.disconnect();
|
||||||
paramBlock1.previousConnection.disconnect();
|
paramBlock1.previousConnection.disconnect();
|
||||||
containerBlock.getInput('STACK').connection.connect(paramBlock2.previousConnection);
|
containerBlock.getInput('STACK').connection.connect(paramBlock2.previousConnection);
|
||||||
paramBlock2.nextConnection.connect(paramBlock1.previousConnection);
|
paramBlock2.nextConnection.connect(paramBlock1.previousConnection);
|
||||||
this.clock.runAll();
|
this.clock.runAll();
|
||||||
|
|
||||||
chai.assert.equal(
|
chai.assert.equal(
|
||||||
callBlock.getInputTargetBlock('ARG0'),
|
callBlock.getInputTargetBlock('ARG0'),
|
||||||
block2,
|
block2,
|
||||||
|
|||||||
@@ -161,14 +161,14 @@ suite('Angle Fields', function() {
|
|||||||
const field = new Blockly.FieldAngle(0, null, {
|
const field = new Blockly.FieldAngle(0, null, {
|
||||||
clockwise: true,
|
clockwise: true,
|
||||||
});
|
});
|
||||||
chai.assert.isTrue(field.clockwise_);
|
chai.assert.isTrue(field.clockwise);
|
||||||
});
|
});
|
||||||
test('JSON Definition', function() {
|
test('JSON Definition', function() {
|
||||||
const field = Blockly.FieldAngle.fromJson({
|
const field = Blockly.FieldAngle.fromJson({
|
||||||
value: 0,
|
value: 0,
|
||||||
clockwise: true,
|
clockwise: true,
|
||||||
});
|
});
|
||||||
chai.assert.isTrue(field.clockwise_);
|
chai.assert.isTrue(field.clockwise);
|
||||||
});
|
});
|
||||||
test('Constant', function() {
|
test('Constant', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -176,7 +176,7 @@ suite('Angle Fields', function() {
|
|||||||
// can do this.
|
// can do this.
|
||||||
Blockly.FieldAngle.CLOCKWISE = true;
|
Blockly.FieldAngle.CLOCKWISE = true;
|
||||||
const field = new Blockly.FieldAngle();
|
const field = new Blockly.FieldAngle();
|
||||||
chai.assert.isTrue(field.clockwise_);
|
chai.assert.isTrue(field.clockwise);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
suite('Offset', function() {
|
suite('Offset', function() {
|
||||||
@@ -184,14 +184,14 @@ suite('Angle Fields', function() {
|
|||||||
const field = new Blockly.FieldAngle(0, null, {
|
const field = new Blockly.FieldAngle(0, null, {
|
||||||
offset: 90,
|
offset: 90,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 90);
|
chai.assert.equal(field.offset, 90);
|
||||||
});
|
});
|
||||||
test('JSON Definition', function() {
|
test('JSON Definition', function() {
|
||||||
const field = Blockly.FieldAngle.fromJson({
|
const field = Blockly.FieldAngle.fromJson({
|
||||||
value: 0,
|
value: 0,
|
||||||
offset: 90,
|
offset: 90,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 90);
|
chai.assert.equal(field.offset, 90);
|
||||||
});
|
});
|
||||||
test('Constant', function() {
|
test('Constant', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -199,7 +199,7 @@ suite('Angle Fields', function() {
|
|||||||
// can do this.
|
// can do this.
|
||||||
Blockly.FieldAngle.OFFSET = 90;
|
Blockly.FieldAngle.OFFSET = 90;
|
||||||
const field = new Blockly.FieldAngle();
|
const field = new Blockly.FieldAngle();
|
||||||
chai.assert.equal(field.offset_, 90);
|
chai.assert.equal(field.offset, 90);
|
||||||
});
|
});
|
||||||
test('Null', function() {
|
test('Null', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -210,7 +210,7 @@ suite('Angle Fields', function() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
offset: null,
|
offset: null,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 90);
|
chai.assert.equal(field.offset, 90);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
suite('Wrap', function() {
|
suite('Wrap', function() {
|
||||||
@@ -218,14 +218,14 @@ suite('Angle Fields', function() {
|
|||||||
const field = new Blockly.FieldAngle(0, null, {
|
const field = new Blockly.FieldAngle(0, null, {
|
||||||
wrap: 180,
|
wrap: 180,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.wrap_, 180);
|
chai.assert.equal(field.wrap, 180);
|
||||||
});
|
});
|
||||||
test('JSON Definition', function() {
|
test('JSON Definition', function() {
|
||||||
const field = Blockly.FieldAngle.fromJson({
|
const field = Blockly.FieldAngle.fromJson({
|
||||||
value: 0,
|
value: 0,
|
||||||
wrap: 180,
|
wrap: 180,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.wrap_, 180);
|
chai.assert.equal(field.wrap, 180);
|
||||||
});
|
});
|
||||||
test('Constant', function() {
|
test('Constant', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -233,7 +233,7 @@ suite('Angle Fields', function() {
|
|||||||
// can do this.
|
// can do this.
|
||||||
Blockly.FieldAngle.WRAP = 180;
|
Blockly.FieldAngle.WRAP = 180;
|
||||||
const field = new Blockly.FieldAngle();
|
const field = new Blockly.FieldAngle();
|
||||||
chai.assert.equal(field.wrap_, 180);
|
chai.assert.equal(field.wrap, 180);
|
||||||
});
|
});
|
||||||
test('Null', function() {
|
test('Null', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -244,7 +244,7 @@ suite('Angle Fields', function() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
wrap: null,
|
wrap: null,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.wrap_, 180);
|
chai.assert.equal(field.wrap, 180);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
suite('Round', function() {
|
suite('Round', function() {
|
||||||
@@ -252,14 +252,14 @@ suite('Angle Fields', function() {
|
|||||||
const field = new Blockly.FieldAngle(0, null, {
|
const field = new Blockly.FieldAngle(0, null, {
|
||||||
round: 30,
|
round: 30,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.round_, 30);
|
chai.assert.equal(field.round, 30);
|
||||||
});
|
});
|
||||||
test('JSON Definition', function() {
|
test('JSON Definition', function() {
|
||||||
const field = Blockly.FieldAngle.fromJson({
|
const field = Blockly.FieldAngle.fromJson({
|
||||||
value: 0,
|
value: 0,
|
||||||
round: 30,
|
round: 30,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.round_, 30);
|
chai.assert.equal(field.round, 30);
|
||||||
});
|
});
|
||||||
test('Constant', function() {
|
test('Constant', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -267,7 +267,7 @@ suite('Angle Fields', function() {
|
|||||||
// can do this.
|
// can do this.
|
||||||
Blockly.FieldAngle.ROUND = 30;
|
Blockly.FieldAngle.ROUND = 30;
|
||||||
const field = new Blockly.FieldAngle();
|
const field = new Blockly.FieldAngle();
|
||||||
chai.assert.equal(field.round_, 30);
|
chai.assert.equal(field.round, 30);
|
||||||
});
|
});
|
||||||
test('Null', function() {
|
test('Null', function() {
|
||||||
// Note: Generally constants should be set at compile time, not
|
// Note: Generally constants should be set at compile time, not
|
||||||
@@ -278,7 +278,7 @@ suite('Angle Fields', function() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
round: null,
|
round: null,
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.round_, 30);
|
chai.assert.equal(field.round, 30);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
suite('Mode', function() {
|
suite('Mode', function() {
|
||||||
@@ -287,16 +287,16 @@ suite('Angle Fields', function() {
|
|||||||
const field = new Blockly.FieldAngle(0, null, {
|
const field = new Blockly.FieldAngle(0, null, {
|
||||||
mode: 'compass',
|
mode: 'compass',
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 90);
|
chai.assert.equal(field.offset, 90);
|
||||||
chai.assert.isTrue(field.clockwise_);
|
chai.assert.isTrue(field.clockwise);
|
||||||
});
|
});
|
||||||
test('JS Configuration', function() {
|
test('JS Configuration', function() {
|
||||||
const field = Blockly.FieldAngle.fromJson({
|
const field = Blockly.FieldAngle.fromJson({
|
||||||
value: 0,
|
value: 0,
|
||||||
mode: 'compass',
|
mode: 'compass',
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 90);
|
chai.assert.equal(field.offset, 90);
|
||||||
chai.assert.isTrue(field.clockwise_);
|
chai.assert.isTrue(field.clockwise);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
suite('Protractor', function() {
|
suite('Protractor', function() {
|
||||||
@@ -304,16 +304,16 @@ suite('Angle Fields', function() {
|
|||||||
const field = new Blockly.FieldAngle(0, null, {
|
const field = new Blockly.FieldAngle(0, null, {
|
||||||
mode: 'protractor',
|
mode: 'protractor',
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 0);
|
chai.assert.equal(field.offset, 0);
|
||||||
chai.assert.isFalse(field.clockwise_);
|
chai.assert.isFalse(field.clockwise);
|
||||||
});
|
});
|
||||||
test('JS Configuration', function() {
|
test('JS Configuration', function() {
|
||||||
const field = Blockly.FieldAngle.fromJson({
|
const field = Blockly.FieldAngle.fromJson({
|
||||||
value: 0,
|
value: 0,
|
||||||
mode: 'protractor',
|
mode: 'protractor',
|
||||||
});
|
});
|
||||||
chai.assert.equal(field.offset_, 0);
|
chai.assert.equal(field.offset, 0);
|
||||||
chai.assert.isFalse(field.clockwise_);
|
chai.assert.isFalse(field.clockwise);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -173,9 +173,9 @@ suite('Colour Fields', function() {
|
|||||||
suite('Customizations', function() {
|
suite('Customizations', function() {
|
||||||
suite('Colours and Titles', function() {
|
suite('Colours and Titles', function() {
|
||||||
function assertColoursAndTitles(field, colours, titles) {
|
function assertColoursAndTitles(field, colours, titles) {
|
||||||
field.dropdownCreate_();
|
field.dropdownCreate();
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let node = field.picker_.firstChild.firstChild;
|
let node = field.picker.firstChild.firstChild;
|
||||||
while (node) {
|
while (node) {
|
||||||
chai.assert.equal(node.getAttribute('title'), titles[index]);
|
chai.assert.equal(node.getAttribute('title'), titles[index]);
|
||||||
chai.assert.equal(
|
chai.assert.equal(
|
||||||
@@ -251,8 +251,8 @@ suite('Colour Fields', function() {
|
|||||||
});
|
});
|
||||||
suite('Columns', function() {
|
suite('Columns', function() {
|
||||||
function assertColumns(field, columns) {
|
function assertColumns(field, columns) {
|
||||||
field.dropdownCreate_();
|
field.dropdownCreate();
|
||||||
chai.assert.equal(field.picker_.firstChild.children.length, columns);
|
chai.assert.equal(field.picker.firstChild.children.length, columns);
|
||||||
}
|
}
|
||||||
test('Constants', function() {
|
test('Constants', function() {
|
||||||
const columns = Blockly.FieldColour.COLUMNS;
|
const columns = Blockly.FieldColour.COLUMNS;
|
||||||
|
|||||||
@@ -189,15 +189,15 @@ suite('Toolbox', function() {
|
|||||||
this.toolbox.dispose();
|
this.toolbox.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
function createKeyDownMock(keyCode) {
|
function createKeyDownMock(key) {
|
||||||
return {
|
return {
|
||||||
'keyCode': keyCode,
|
'key': key,
|
||||||
'preventDefault': function() {},
|
'preventDefault': function() {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCorrectFunctionCalled(toolbox, keyCode, funcName) {
|
function testCorrectFunctionCalled(toolbox, key, funcName) {
|
||||||
const event = createKeyDownMock(keyCode);
|
const event = createKeyDownMock(key);
|
||||||
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
||||||
const selectMethodStub = sinon.stub(toolbox, funcName);
|
const selectMethodStub = sinon.stub(toolbox, funcName);
|
||||||
selectMethodStub.returns(true);
|
selectMethodStub.returns(true);
|
||||||
@@ -207,21 +207,21 @@ suite('Toolbox', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('Down button is pushed -> Should call selectNext_', function() {
|
test('Down button is pushed -> Should call selectNext_', function() {
|
||||||
testCorrectFunctionCalled(this.toolbox, Blockly.utils.KeyCodes.DOWN, 'selectNext_', true);
|
testCorrectFunctionCalled(this.toolbox, 'ArrowDown', 'selectNext_', true);
|
||||||
});
|
});
|
||||||
test('Up button is pushed -> Should call selectPrevious_', function() {
|
test('Up button is pushed -> Should call selectPrevious_', function() {
|
||||||
testCorrectFunctionCalled(this.toolbox, Blockly.utils.KeyCodes.UP, 'selectPrevious_', true);
|
testCorrectFunctionCalled(this.toolbox, 'ArrowUp', 'selectPrevious_', true);
|
||||||
});
|
});
|
||||||
test('Left button is pushed -> Should call selectParent_', function() {
|
test('Left button is pushed -> Should call selectParent_', function() {
|
||||||
testCorrectFunctionCalled(this.toolbox, Blockly.utils.KeyCodes.LEFT, 'selectParent_', true);
|
testCorrectFunctionCalled(this.toolbox, 'ArrowLeft', 'selectParent_', true);
|
||||||
});
|
});
|
||||||
test('Right button is pushed -> Should call selectChild_', function() {
|
test('Right button is pushed -> Should call selectChild_', function() {
|
||||||
testCorrectFunctionCalled(this.toolbox, Blockly.utils.KeyCodes.RIGHT, 'selectChild_', true);
|
testCorrectFunctionCalled(this.toolbox, 'ArrowRight', 'selectChild_', true);
|
||||||
});
|
});
|
||||||
test('Enter button is pushed -> Should toggle expandedd', function() {
|
test('Enter button is pushed -> Should toggle expanded', function() {
|
||||||
this.toolbox.selectedItem_ = getCollapsibleItem(this.toolbox);
|
this.toolbox.selectedItem_ = getCollapsibleItem(this.toolbox);
|
||||||
const toggleExpandedStub = sinon.stub(this.toolbox.selectedItem_, 'toggleExpanded');
|
const toggleExpandedStub = sinon.stub(this.toolbox.selectedItem_, 'toggleExpanded');
|
||||||
const event = createKeyDownMock(Blockly.utils.KeyCodes.ENTER);
|
const event = createKeyDownMock('Enter');
|
||||||
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
||||||
this.toolbox.onKeyDown_(event);
|
this.toolbox.onKeyDown_(event);
|
||||||
sinon.assert.called(toggleExpandedStub);
|
sinon.assert.called(toggleExpandedStub);
|
||||||
@@ -229,7 +229,7 @@ suite('Toolbox', function() {
|
|||||||
});
|
});
|
||||||
test('Enter button is pushed when no item is selected -> Should not call prevent default', function() {
|
test('Enter button is pushed when no item is selected -> Should not call prevent default', function() {
|
||||||
this.toolbox.selectedItem_ = null;
|
this.toolbox.selectedItem_ = null;
|
||||||
const event = createKeyDownMock(Blockly.utils.KeyCodes.ENTER);
|
const event = createKeyDownMock('Enter');
|
||||||
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
const preventDefaultEvent = sinon.stub(event, 'preventDefault');
|
||||||
this.toolbox.onKeyDown_(event);
|
this.toolbox.onKeyDown_(event);
|
||||||
sinon.assert.notCalled(preventDefaultEvent);
|
sinon.assert.notCalled(preventDefaultEvent);
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ suite('Variable Map', function() {
|
|||||||
const variable =
|
const variable =
|
||||||
this.variableMap.createVariable('test name', 'test type', 'test id');
|
this.variableMap.createVariable('test name', 'test type', 'test id');
|
||||||
this.variableMap.deleteVariable(variable);
|
this.variableMap.deleteVariable(variable);
|
||||||
|
|
||||||
assertEventFired(
|
assertEventFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarDelete,
|
Blockly.Events.VarDelete,
|
||||||
@@ -320,7 +320,7 @@ suite('Variable Map', function() {
|
|||||||
new Blockly.VariableModel(
|
new Blockly.VariableModel(
|
||||||
this.workspace, 'test name', 'test type', 'test id');
|
this.workspace, 'test name', 'test type', 'test id');
|
||||||
this.variableMap.deleteVariable(variable);
|
this.variableMap.deleteVariable(variable);
|
||||||
|
|
||||||
assertEventNotFired(
|
assertEventNotFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarDelete,
|
Blockly.Events.VarDelete,
|
||||||
@@ -335,7 +335,7 @@ suite('Variable Map', function() {
|
|||||||
function() {
|
function() {
|
||||||
this.variableMap.createVariable('test name', 'test type', 'test id');
|
this.variableMap.createVariable('test name', 'test type', 'test id');
|
||||||
this.variableMap.deleteVariableById('test id');
|
this.variableMap.deleteVariableById('test id');
|
||||||
|
|
||||||
assertEventFired(
|
assertEventFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarDelete,
|
Blockly.Events.VarDelete,
|
||||||
@@ -351,7 +351,7 @@ suite('Variable Map', function() {
|
|||||||
'delete events are not fired when a variable does not exist',
|
'delete events are not fired when a variable does not exist',
|
||||||
function() {
|
function() {
|
||||||
this.variableMap.deleteVariableById('test id');
|
this.variableMap.deleteVariableById('test id');
|
||||||
|
|
||||||
assertEventNotFired(
|
assertEventNotFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarDelete,
|
Blockly.Events.VarDelete,
|
||||||
@@ -379,7 +379,7 @@ suite('Variable Map', function() {
|
|||||||
},
|
},
|
||||||
this.workspace.id);
|
this.workspace.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'rename events are not fired if the variable name already matches',
|
'rename events are not fired if the variable name already matches',
|
||||||
function() {
|
function() {
|
||||||
@@ -387,14 +387,14 @@ suite('Variable Map', function() {
|
|||||||
this.variableMap.createVariable(
|
this.variableMap.createVariable(
|
||||||
'test name', 'test type', 'test id');
|
'test name', 'test type', 'test id');
|
||||||
this.variableMap.renameVariable(variable, 'test name');
|
this.variableMap.renameVariable(variable, 'test name');
|
||||||
|
|
||||||
assertEventNotFired(
|
assertEventNotFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarRename,
|
Blockly.Events.VarRename,
|
||||||
{},
|
{},
|
||||||
this.workspace.id);
|
this.workspace.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'rename events are not fired if the variable does not exist',
|
'rename events are not fired if the variable does not exist',
|
||||||
function() {
|
function() {
|
||||||
@@ -402,7 +402,7 @@ suite('Variable Map', function() {
|
|||||||
new Blockly.VariableModel(
|
new Blockly.VariableModel(
|
||||||
'test name', 'test type', 'test id');
|
'test name', 'test type', 'test id');
|
||||||
this.variableMap.renameVariable(variable, 'test name');
|
this.variableMap.renameVariable(variable, 'test name');
|
||||||
|
|
||||||
assertEventNotFired(
|
assertEventNotFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarRename,
|
Blockly.Events.VarRename,
|
||||||
@@ -426,21 +426,21 @@ suite('Variable Map', function() {
|
|||||||
},
|
},
|
||||||
this.workspace.id);
|
this.workspace.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'rename events are not fired if the variable name already matches',
|
'rename events are not fired if the variable name already matches',
|
||||||
function() {
|
function() {
|
||||||
this.variableMap.createVariable(
|
this.variableMap.createVariable(
|
||||||
'test name', 'test type', 'test id');
|
'test name', 'test type', 'test id');
|
||||||
this.variableMap.renameVariableById('test id', 'test name');
|
this.variableMap.renameVariableById('test id', 'test name');
|
||||||
|
|
||||||
assertEventNotFired(
|
assertEventNotFired(
|
||||||
this.eventSpy,
|
this.eventSpy,
|
||||||
Blockly.Events.VarRename,
|
Blockly.Events.VarRename,
|
||||||
{},
|
{},
|
||||||
this.workspace.id);
|
this.workspace.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'renaming throws if the variable does not exist',
|
'renaming throws if the variable does not exist',
|
||||||
function() {
|
function() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ async function runMochaTestsInBrowser() {
|
|||||||
],
|
],
|
||||||
logLevel: 'warn',
|
logLevel: 'warn',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run in headless mode on Github Actions.
|
// Run in headless mode on Github Actions.
|
||||||
if (process.env.CI) {
|
if (process.env.CI) {
|
||||||
options.capabilities['goog:chromeOptions'].args.push(
|
options.capabilities['goog:chromeOptions'].args.push(
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class FieldMitosis extends Field<CellGroup> {
|
|||||||
doMitosis(): void {
|
doMitosis(): void {
|
||||||
const cellGroup = this.getValue();
|
const cellGroup = this.getValue();
|
||||||
if (!cellGroup) return;
|
if (!cellGroup) return;
|
||||||
|
|
||||||
const cells = cellGroup.cells.flatMap((cell) => {
|
const cells = cellGroup.cells.flatMap((cell) => {
|
||||||
const leftCell: Cell = {cellId: `${cell.cellId}-left`};
|
const leftCell: Cell = {cellId: `${cell.cellId}-left`};
|
||||||
const rightCell: Cell = {cellId: `${cell.cellId}-right`};
|
const rightCell: Cell = {cellId: `${cell.cellId}-right`};
|
||||||
|
|||||||
Reference in New Issue
Block a user