From fa925e8c3574de4b9ef843d9c039f647061a4373 Mon Sep 17 00:00:00 2001 From: Blake Thomas Williams <49404493+btw17@users.noreply.github.com> Date: Fri, 30 Sep 2022 10:53:19 -0500 Subject: [PATCH] chore: cleaned up several type cases in core/field.ts and impacted files (#6363) * chore: cleaned up several type cases in core/field.ts and impacted files * chore: updated instances of `sourceBlock_!` to `getSourceBlock()` * chore: updated instances of `fieldGroup_!` to `getSvgRoot()` * chore: updated nullable variables in `field.ts` to use internal functions * chore: updated getSourceBlock and getSvgRoot to handle nullability * chore: updated comments to reference throwing an error * fix: reverted `getSvgRoot` to `fieldGroup_` in null-accepting areas * fix: updated `getSvgRoot` to allow null and added null handling methods * fix: moved click target error handling to their specific cases * fix: updated drawer.ts to handle cast svg group to defined --- core/field.ts | 172 ++++++++++++++++++-------------- core/field_angle.ts | 6 +- core/field_checkbox.ts | 5 +- core/field_colour.ts | 2 +- core/field_dropdown.ts | 41 ++++---- core/field_label.ts | 2 +- core/field_multilineinput.ts | 6 +- core/field_textinput.ts | 24 +++-- core/field_variable.ts | 13 +-- core/renderers/common/drawer.ts | 2 +- core/tooltip.ts | 2 +- 11 files changed, 151 insertions(+), 124 deletions(-) diff --git a/core/field.ts b/core/field.ts index 9f29e6d1f..ac827bbd9 100644 --- a/core/field.ts +++ b/core/field.ts @@ -72,9 +72,7 @@ export abstract class Field implements IASTNodeLocationSvg, protected value_: AnyDuringMigration; /** Validation function called when user edits an editable field. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'Function'. - protected validator_: Function = null as AnyDuringMigration; + protected validator_: Function|null = null; /** * Used to cache the field's tooltip value if setTooltip is called when the @@ -87,44 +85,31 @@ export abstract class Field implements IASTNodeLocationSvg, * Holds the cursors svg element when the cursor is attached to the field. * This is null if there is no cursor on the field. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGElement'. - private cursorSvg_: SVGElement = null as AnyDuringMigration; + private cursorSvg_: SVGElement|null = null; /** * Holds the markers svg element when the marker is attached to the field. * This is null if there is no marker on the field. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGElement'. - private markerSvg_: SVGElement = null as AnyDuringMigration; + private markerSvg_: SVGElement|null = null; /** The rendered field's SVG group element. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGGElement'. - protected fieldGroup_: SVGGElement = null as AnyDuringMigration; + protected fieldGroup_: SVGGElement|null = null; /** The rendered field's SVG border element. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGRectElement'. - protected borderRect_: SVGRectElement = null as AnyDuringMigration; + protected borderRect_: SVGRectElement|null = null; /** The rendered field's SVG text element. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGTextElement'. - protected textElement_: SVGTextElement = null as AnyDuringMigration; + protected textElement_: SVGTextElement|null = null; /** The rendered field's text content element. */ - // AnyDuringMigration because: Type 'null' is not assignable to type 'Text'. - protected textContent_: Text = null as AnyDuringMigration; + protected textContent_: Text|null = null; /** Mouse down event listener data. */ private mouseDownWrapper_: browserEvents.Data|null = null; /** Constants associated with the source block's renderer. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'ConstantProvider'. - protected constants_: ConstantProvider = null as AnyDuringMigration; + protected constants_: ConstantProvider|null = null; /** * Has this field been disposed of? @@ -137,8 +122,7 @@ export abstract class Field implements IASTNodeLocationSvg, maxDisplayLength = 50; /** Block this field is attached to. Starts as null, then set in init. */ - // AnyDuringMigration because: Type 'null' is not assignable to type 'Block'. - protected sourceBlock_: Block = null as AnyDuringMigration; + protected sourceBlock_: Block|null = null; /** Does this block need to be re-rendered? */ protected isDirty_ = true; @@ -152,9 +136,7 @@ export abstract class Field implements IASTNodeLocationSvg, protected enabled_ = true; /** The element the click handler is bound to. */ - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'Element'. - protected clickTarget_: Element = null as AnyDuringMigration; + protected clickTarget_: Element|null = null; /** * The prefix field. @@ -271,8 +253,12 @@ export abstract class Field implements IASTNodeLocationSvg, * Get the block this field is attached to. * * @returns The block containing this field. + * @throws An error if the source block is not defined. */ getSourceBlock(): Block { + if (!this.sourceBlock_) { + throw new Error(`The source block is ${this.sourceBlock_}.`); + } return this.sourceBlock_; } @@ -361,9 +347,11 @@ export abstract class Field implements IASTNodeLocationSvg, * do custom input handling. */ protected bindEvents_() { - Tooltip.bindMouseEvents(this.getClickTarget_()); + const clickTarget = this.getClickTarget_(); + if (!clickTarget) throw new Error('A click target has not been set.'); + Tooltip.bindMouseEvents(clickTarget); this.mouseDownWrapper_ = browserEvents.conditionalBind( - this.getClickTarget_(), 'mousedown', this, this.onMouseDown_); + clickTarget, 'mousedown', this, this.onMouseDown_); } /** @@ -431,7 +419,7 @@ export abstract class Field implements IASTNodeLocationSvg, * Used to see if `this` has overridden any relevant hooks. * @returns The stringified version of the XML state, or null. */ - protected saveLegacyState(callingClass: AnyDuringMigration): string|null { + protected saveLegacyState(callingClass: FieldProto): string|null { if (callingClass.prototype.saveState === this.saveState && callingClass.prototype.toXml !== this.toXml) { const elem = utilsXml.createElement('field'); @@ -454,7 +442,7 @@ export abstract class Field implements IASTNodeLocationSvg, * @param state The state to apply to the field. * @returns Whether the state was applied or not. */ - loadLegacyState(callingClass: AnyDuringMigration, state: AnyDuringMigration): + loadLegacyState(callingClass: FieldProto, state: AnyDuringMigration): boolean { if (callingClass.prototype.loadState === this.loadState && callingClass.prototype.fromXml !== this.fromXml) { @@ -491,7 +479,7 @@ export abstract class Field implements IASTNodeLocationSvg, if (!this.EDITABLE || !group) { return; } - if (this.enabled_ && this.sourceBlock_.isEditable()) { + if (this.enabled_ && this.getSourceBlock().isEditable()) { dom.addClass(group, 'blocklyEditableText'); dom.removeClass(group, 'blocklyNonEditableText'); group.style.cursor = this.CURSOR; @@ -590,7 +578,7 @@ export abstract class Field implements IASTNodeLocationSvg, return; } this.visible_ = visible; - const root = this.getSvgRoot(); + const root = this.fieldGroup_; if (root) { root.style.display = visible ? 'block' : 'none'; } @@ -630,10 +618,49 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns The group element. */ - getSvgRoot(): SVGGElement { + getSvgRoot(): SVGGElement|null { return this.fieldGroup_; } + /** + * Gets the border rectangle element. + * + * @returns The border rectangle element. + * @throws An error if the border rectangle element is not defined. + */ + protected getBorderRect(): SVGRectElement { + if (!this.borderRect_) { + throw new Error(`The border rectangle is ${this.borderRect_}.`); + } + return this.borderRect_; + } + + /** + * Gets the text element. + * + * @returns The text element. + * @throws An error if the text element is not defined. + */ + protected getTextElement(): SVGTextElement { + if (!this.textElement_) { + throw new Error(`The text element is ${this.textElement_}.`); + } + return this.textElement_; + } + + /** + * Gets the text content. + * + * @returns The text content. + * @throws An error if the text content is not defined. + */ + protected getTextContent(): Text { + if (!this.textContent_) { + throw new Error(`The text content is ${this.textContent_}.`); + } + return this.textContent_; + } + /** * Updates the field to match the colour/style of the block. Should only be * called by BlockSvg.applyColour(). @@ -726,20 +753,19 @@ export abstract class Field implements IASTNodeLocationSvg, const constants = this.getConstants(); const halfHeight = this.size_.height / 2; - // AnyDuringMigration because: Argument of type 'number' is not assignable - // to parameter of type 'string'. this.textElement_.setAttribute( 'x', - (this.sourceBlock_.RTL ? this.size_.width - contentWidth - xOffset : - xOffset) as AnyDuringMigration); - // AnyDuringMigration because: Argument of type 'number' is not assignable - // to parameter of type 'string'. + `${ + this.getSourceBlock().RTL ? + this.size_.width - contentWidth - xOffset : + xOffset}`); this.textElement_.setAttribute( 'y', - (constants!.FIELD_TEXT_BASELINE_CENTER ? - halfHeight : - halfHeight - constants!.FIELD_TEXT_HEIGHT / 2 + - constants!.FIELD_TEXT_BASELINE) as AnyDuringMigration); + `${ + constants!.FIELD_TEXT_BASELINE_CENTER ? + halfHeight : + halfHeight - constants!.FIELD_TEXT_HEIGHT / 2 + + constants!.FIELD_TEXT_BASELINE}`); } /** Position a field's border rect after a size change. */ @@ -747,24 +773,12 @@ export abstract class Field implements IASTNodeLocationSvg, if (!this.borderRect_) { return; } - // AnyDuringMigration because: Argument of type 'number' is not assignable - // to parameter of type 'string'. + this.borderRect_.setAttribute('width', `${this.size_.width}`); + this.borderRect_.setAttribute('height', `${this.size_.height}`); this.borderRect_.setAttribute( - 'width', this.size_.width as AnyDuringMigration); - // AnyDuringMigration because: Argument of type 'number' is not assignable - // to parameter of type 'string'. + 'rx', `${this.getConstants()!.FIELD_BORDER_RECT_RADIUS}`); this.borderRect_.setAttribute( - 'height', this.size_.height as AnyDuringMigration); - // AnyDuringMigration because: Argument of type 'number' is not assignable - // to parameter of type 'string'. - this.borderRect_.setAttribute( - 'rx', - this.getConstants()!.FIELD_BORDER_RECT_RADIUS as AnyDuringMigration); - // AnyDuringMigration because: Argument of type 'number' is not assignable - // to parameter of type 'string'. - this.borderRect_.setAttribute( - 'ry', - this.getConstants()!.FIELD_BORDER_RECT_RADIUS as AnyDuringMigration); + 'ry', `${this.getConstants()!.FIELD_BORDER_RECT_RADIUS}`); } /** @@ -810,7 +824,7 @@ export abstract class Field implements IASTNodeLocationSvg, // - Webkit / Blink: fill-box / object bounding box // - Gecko: stroke-box const bBox = (this.sourceBlock_ as BlockSvg).getHeightWidth(); - const scale = (this.sourceBlock_.workspace as WorkspaceSvg).scale; + const scale = (this.getSourceBlock().workspace as WorkspaceSvg).scale; xy = this.getAbsoluteXY_(); scaledWidth = (bBox.width + 1) * scale; scaledHeight = (bBox.height + 1) * scale; @@ -896,9 +910,7 @@ export abstract class Field implements IASTNodeLocationSvg, */ markDirty() { this.isDirty_ = true; - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'ConstantProvider'. - this.constants_ = null as AnyDuringMigration; + this.constants_ = null; } /** @@ -1101,7 +1113,7 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns Element to bind click handler to. */ - protected getClickTarget_(): Element { + protected getClickTarget_(): Element|null { return this.clickTarget_ || this.getSvgRoot(); } @@ -1145,7 +1157,7 @@ export abstract class Field implements IASTNodeLocationSvg, */ getParentInput(): Input { let parentInput = null; - const block = this.sourceBlock_; + const block = this.getSourceBlock(); const inputs = block.inputList; for (let idx = 0; idx < block.inputList.length; idx++) { @@ -1158,9 +1170,7 @@ export abstract class Field implements IASTNodeLocationSvg, } } } - // AnyDuringMigration because: Type 'Input | null' is not assignable to - // type 'Input'. - return parentInput as AnyDuringMigration; + return parentInput!; } /** @@ -1199,12 +1209,13 @@ export abstract class Field implements IASTNodeLocationSvg, */ setCursorSvg(cursorSvg: SVGElement) { if (!cursorSvg) { - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGElement'. - this.cursorSvg_ = null as AnyDuringMigration; + this.cursorSvg_ = null; return; } + if (!this.fieldGroup_) { + throw new Error(`The field group is ${this.fieldGroup_}.`); + } this.fieldGroup_.appendChild(cursorSvg); this.cursorSvg_ = cursorSvg; } @@ -1217,19 +1228,20 @@ export abstract class Field implements IASTNodeLocationSvg, */ setMarkerSvg(markerSvg: SVGElement) { if (!markerSvg) { - // AnyDuringMigration because: Type 'null' is not assignable to type - // 'SVGElement'. - this.markerSvg_ = null as AnyDuringMigration; + this.markerSvg_ = null; return; } + if (!this.fieldGroup_) { + throw new Error(`The field group is ${this.fieldGroup_}.`); + } this.fieldGroup_.appendChild(markerSvg); this.markerSvg_ = markerSvg; } /** Redraw any attached marker or cursor svgs if needed. */ protected updateMarkers_() { - const workspace = this.sourceBlock_.workspace as WorkspaceSvg; + const workspace = this.getSourceBlock().workspace as WorkspaceSvg; if (workspace.keyboardAccessibilityMode && this.cursorSvg_) { workspace.getCursor()!.draw(); } @@ -1246,3 +1258,9 @@ export abstract class Field implements IASTNodeLocationSvg, export interface FieldConfig { tooltip?: string; } + +/** + * For use by Field and descendants of Field. Constructors can change + * in descendants, though they should contain all of Field's prototype methods. + */ +export type FieldProto = Pick; diff --git a/core/field_angle.ts b/core/field_angle.ts index 3ae5e753f..ecbfdb66f 100644 --- a/core/field_angle.ts +++ b/core/field_angle.ts @@ -202,7 +202,7 @@ export class FieldAngle extends FieldTextInput { // #2380) this.symbol_ = dom.createSvgElement(Svg.TSPAN, {}); this.symbol_.appendChild(document.createTextNode('°')); - this.textElement_.appendChild(this.symbol_); + this.getTextElement().appendChild(this.symbol_); } /** Updates the graph when the field rerenders. */ @@ -436,12 +436,12 @@ export class FieldAngle extends FieldTextInput { // 'Event'. if ((e as AnyDuringMigration).keyCode === KeyCodes.LEFT) { // decrement (increment in RTL) - multiplier = this.sourceBlock_.RTL ? 1 : -1; + multiplier = this.getSourceBlock().RTL ? 1 : -1; // AnyDuringMigration because: Property 'keyCode' does not exist on type // 'Event'. } else if ((e as AnyDuringMigration).keyCode === KeyCodes.RIGHT) { // increment (decrement in RTL) - multiplier = this.sourceBlock_.RTL ? -1 : 1; + multiplier = this.getSourceBlock().RTL ? -1 : 1; // AnyDuringMigration because: Property 'keyCode' does not exist on type // 'Event'. } else if ((e as AnyDuringMigration).keyCode === KeyCodes.DOWN) { diff --git a/core/field_checkbox.ts b/core/field_checkbox.ts index 765705f1a..f8ac67aae 100644 --- a/core/field_checkbox.ts +++ b/core/field_checkbox.ts @@ -112,8 +112,9 @@ export class FieldCheckbox extends Field { override initView() { super.initView(); - dom.addClass(this.textElement_, 'blocklyCheckbox'); - this.textElement_.style.display = this.value_ ? 'block' : 'none'; + const textElement = this.getTextElement(); + dom.addClass(textElement, 'blocklyCheckbox'); + textElement.style.display = this.value_ ? 'block' : 'none'; } override render_() { diff --git a/core/field_colour.ts b/core/field_colour.ts index f70843053..aa1a5b9f0 100644 --- a/core/field_colour.ts +++ b/core/field_colour.ts @@ -191,7 +191,7 @@ export class FieldColour extends Field { this.getConstants()!.FIELD_COLOUR_DEFAULT_HEIGHT); if (!this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) { this.createBorderRect_(); - this.borderRect_.style['fillOpacity'] = '1'; + this.getBorderRect().style['fillOpacity'] = '1'; } else if (this.sourceBlock_ instanceof BlockSvg) { this.clickTarget_ = this.sourceBlock_.getSvgRoot(); } diff --git a/core/field_dropdown.ts b/core/field_dropdown.ts index 8c274f8a1..c58e7376d 100644 --- a/core/field_dropdown.ts +++ b/core/field_dropdown.ts @@ -217,24 +217,24 @@ export class FieldDropdown extends Field { protected shouldAddBorderRect_(): boolean { return !this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW && - !this.sourceBlock_.isShadow(); + !this.getSourceBlock().isShadow(); } /** Create a tspan based arrow. */ protected createTextArrow_() { this.arrow_ = dom.createSvgElement(Svg.TSPAN, {}, this.textElement_); this.arrow_!.appendChild(document.createTextNode( - this.sourceBlock_.RTL ? FieldDropdown.ARROW_CHAR + ' ' : - ' ' + FieldDropdown.ARROW_CHAR)); - if (this.sourceBlock_.RTL) { + this.getSourceBlock().RTL ? FieldDropdown.ARROW_CHAR + ' ' : + ' ' + FieldDropdown.ARROW_CHAR)); + if (this.getSourceBlock().RTL) { // AnyDuringMigration because: Argument of type 'SVGTSpanElement | null' // is not assignable to parameter of type 'Node'. - this.textElement_.insertBefore( + this.getTextElement().insertBefore( this.arrow_ as AnyDuringMigration, this.textContent_); } else { // AnyDuringMigration because: Argument of type 'SVGTSpanElement | null' // is not assignable to parameter of type 'Node'. - this.textElement_.appendChild(this.arrow_ as AnyDuringMigration); + this.getTextElement().appendChild(this.arrow_ as AnyDuringMigration); } } @@ -279,11 +279,11 @@ export class FieldDropdown extends Field { dom.addClass(menuElement, 'blocklyDropdownMenu'); if (this.getConstants()!.FIELD_DROPDOWN_COLOURED_DIV) { - const primaryColour = this.sourceBlock_.isShadow() ? - this.sourceBlock_.getParent()!.getColour() : - this.sourceBlock_.getColour(); - const borderColour = this.sourceBlock_.isShadow() ? - (this.sourceBlock_.getParent() as BlockSvg).style.colourTertiary : + const primaryColour = this.getSourceBlock().isShadow() ? + this.getSourceBlock().getParent()!.getColour() : + this.getSourceBlock().getColour(); + const borderColour = this.getSourceBlock().isShadow() ? + (this.getSourceBlock().getParent() as BlockSvg).style.colourTertiary : (this.sourceBlock_ as BlockSvg).style.colourTertiary; if (!borderColour) { throw new Error( @@ -326,7 +326,7 @@ export class FieldDropdown extends Field { } const menuItem = new MenuItem(content, value); menuItem.setRole(aria.Role.OPTION); - menuItem.setRightToLeft(this.sourceBlock_.RTL); + menuItem.setRightToLeft(this.getSourceBlock().RTL); menuItem.setCheckable(true); menu.addChild(menuItem); menuItem.setChecked(value === this.value_); @@ -532,7 +532,7 @@ export class FieldDropdown extends Field { /** Draws the border with the correct width. */ protected override render_() { // Hide both elements. - this.textContent_.nodeValue = ''; + this.getTextContent().nodeValue = ''; this.imageElement_!.style.display = 'none'; // Show correct element. @@ -590,12 +590,12 @@ export class FieldDropdown extends Field { this.size_.height = height; let arrowX = 0; - if (this.sourceBlock_.RTL) { + if (this.getSourceBlock().RTL) { const imageX = xPadding + arrowWidth; this.imageElement_!.setAttribute('x', imageX.toString()); } else { arrowX = imageWidth + arrowWidth; - this.textElement_.setAttribute('text-anchor', 'end'); + this.getTextElement().setAttribute('text-anchor', 'end'); this.imageElement_!.setAttribute('x', xPadding.toString()); } this.imageElement_!.setAttribute( @@ -607,9 +607,10 @@ export class FieldDropdown extends Field { /** Renders the selected option, which must be text. */ private renderSelectedText_() { // Retrieves the selected option to display through getText_. - this.textContent_.nodeValue = this.getDisplayText_(); - dom.addClass(this.textElement_, 'blocklyDropdownText'); - this.textElement_.setAttribute('text-anchor', 'start'); + this.getTextContent().nodeValue = this.getDisplayText_(); + const textElement = this.getTextElement(); + dom.addClass(textElement, 'blocklyDropdownText'); + textElement.setAttribute('text-anchor', 'start'); // Height and width include the border rect. const hasBorder = !!this.borderRect_; @@ -617,7 +618,7 @@ export class FieldDropdown extends Field { hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, this.getConstants()!.FIELD_TEXT_HEIGHT); const textWidth = dom.getFastTextWidth( - this.textElement_, this.getConstants()!.FIELD_TEXT_FONTSIZE, + this.getTextElement(), this.getConstants()!.FIELD_TEXT_FONTSIZE, this.getConstants()!.FIELD_TEXT_FONTWEIGHT, this.getConstants()!.FIELD_TEXT_FONTFAMILY); const xPadding = @@ -650,7 +651,7 @@ export class FieldDropdown extends Field { hasBorder ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING : 0; const textPadding = this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_PADDING; const svgArrowSize = this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE; - const arrowX = this.sourceBlock_.RTL ? xPadding : x + textPadding; + const arrowX = this.getSourceBlock().RTL ? xPadding : x + textPadding; this.svgArrow_.setAttribute( 'transform', 'translate(' + arrowX + ',' + y + ')'); return svgArrowSize + textPadding; diff --git a/core/field_label.ts b/core/field_label.ts index fc77cb144..c35e29157 100644 --- a/core/field_label.ts +++ b/core/field_label.ts @@ -76,7 +76,7 @@ export class FieldLabel extends Field { override initView() { this.createTextElement_(); if (this.class_) { - dom.addClass(this.textElement_, this.class_); + dom.addClass(this.getTextElement(), this.class_); } } diff --git a/core/field_multilineinput.ts b/core/field_multilineinput.ts index 569d04787..54942f0b5 100644 --- a/core/field_multilineinput.ts +++ b/core/field_multilineinput.ts @@ -189,8 +189,8 @@ export class FieldMultilineInput extends FieldTextInput { textLines += '\n'; } } - if (this.sourceBlock_.RTL) { - // The SVG is LTR, force value to be RTL by adding an RLM. + if (this.getSourceBlock().RTL) { + // The SVG is LTR, force value to be RTL. textLines += '\u200F'; } return textLines; @@ -248,7 +248,7 @@ export class FieldMultilineInput extends FieldTextInput { this.updateSize_(); if (this.isBeingEdited_) { - if (this.sourceBlock_.RTL) { + if (this.getSourceBlock().RTL) { // in RTL, we need to let the browser reflow before resizing // in order to get the correct bounding box of the borderRect // avoiding issue #2777. diff --git a/core/field_textinput.ts b/core/field_textinput.ts index 7904f84f4..dc4e29455 100644 --- a/core/field_textinput.ts +++ b/core/field_textinput.ts @@ -133,7 +133,7 @@ export class FieldTextInput extends Field { let nFields = 0; let nConnections = 0; // Count the number of fields, excluding text fields - for (let i = 0, input; input = this.sourceBlock_.inputList[i]; i++) { + for (let i = 0, input; input = this.getSourceBlock().inputList[i]; i++) { for (let j = 0; input.fieldRow[j]; j++) { nFields++; } @@ -143,8 +143,8 @@ export class FieldTextInput extends Field { } // The special case is when this is the only non-label field on the block // and it has an output but no inputs. - this.fullBlockClickTarget_ = - nFields <= 1 && this.sourceBlock_.outputConnection && !nConnections; + this.fullBlockClickTarget_ = nFields <= 1 && + this.getSourceBlock().outputConnection && !nConnections; } else { this.fullBlockClickTarget_ = false; } @@ -311,7 +311,8 @@ export class FieldTextInput extends Field { * @param quietInput True if editor should be created without focus. */ private showInlineEditor_(quietInput: boolean) { - WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); + WidgetDiv.show( + this, this.getSourceBlock().RTL, this.widgetDispose_.bind(this)); this.htmlInput_ = this.widgetCreate_() as HTMLInputElement; this.isBeingEdited_ = true; @@ -332,7 +333,9 @@ export class FieldTextInput extends Field { eventUtils.setGroup(true); const div = WidgetDiv.getDiv(); - dom.addClass(this.getClickTarget_(), 'editing'); + const clickTarget = this.getClickTarget_(); + if (!clickTarget) throw new Error('A click target has not been set.'); + dom.addClass(clickTarget, 'editing'); const htmlInput = (document.createElement('input')); htmlInput.className = 'blocklyHtmlInput'; @@ -352,8 +355,8 @@ export class FieldTextInput extends Field { // Override border radius. borderRadius = (bBox.bottom - bBox.top) / 2 + 'px'; // Pull stroke colour from the existing shadow block - const strokeColour = this.sourceBlock_.getParent() ? - (this.sourceBlock_.getParent() as BlockSvg).style.colourTertiary : + const strokeColour = this.getSourceBlock().getParent() ? + (this.getSourceBlock().getParent() as BlockSvg).style.colourTertiary : (this.sourceBlock_ as BlockSvg).style.colourTertiary; htmlInput.style.border = 1 * scale + 'px solid ' + strokeColour; div!.style.borderRadius = borderRadius; @@ -401,7 +404,9 @@ export class FieldTextInput extends Field { style.boxShadow = ''; this.htmlInput_ = null; - dom.removeClass(this.getClickTarget_(), 'editing'); + const clickTarget = this.getClickTarget_(); + if (!clickTarget) throw new Error('A click target has not been set.'); + dom.removeClass(clickTarget, 'editing'); } /** @@ -516,7 +521,8 @@ export class FieldTextInput extends Field { // In RTL mode block fields and LTR input fields the left edge moves, // whereas the right edge is fixed. Reposition the editor. - const x = this.sourceBlock_.RTL ? bBox.right - div!.offsetWidth : bBox.left; + const x = + this.getSourceBlock().RTL ? bBox.right - div!.offsetWidth : bBox.left; const xy = new Coordinate(x, bBox.top); div!.style.left = xy.x + 'px'; diff --git a/core/field_variable.ts b/core/field_variable.ts index 2c31cf13a..e337356bf 100644 --- a/core/field_variable.ts +++ b/core/field_variable.ts @@ -139,7 +139,7 @@ export class FieldVariable extends FieldDropdown { return; // Initialization already happened. } const variable = Variables.getOrCreateVariablePackage( - this.sourceBlock_.workspace, null, this.defaultVariableName, + this.getSourceBlock().workspace, null, this.defaultVariableName, this.defaultType_); // Don't call setValue because we don't want to cause a rerender. this.doValueUpdate_(variable.getId()); @@ -148,7 +148,7 @@ export class FieldVariable extends FieldDropdown { override shouldAddBorderRect_() { return super.shouldAddBorderRect_() && (!this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || - this.sourceBlock_.type !== 'variables_get'); + this.getSourceBlock().type !== 'variables_get'); } /** @@ -168,7 +168,7 @@ export class FieldVariable extends FieldDropdown { // AnyDuringMigration because: Argument of type 'string | null' is not // assignable to parameter of type 'string | undefined'. const variable = Variables.getOrCreateVariablePackage( - this.sourceBlock_.workspace, id, variableName as AnyDuringMigration, + this.getSourceBlock().workspace, id, variableName as AnyDuringMigration, variableType); // This should never happen :) @@ -238,7 +238,7 @@ export class FieldVariable extends FieldDropdown { } // This is necessary so that blocks in the flyout can have custom var names. const variable = Variables.getOrCreateVariablePackage( - this.sourceBlock_.workspace, state['id'] || null, state['name'], + this.getSourceBlock().workspace, state['id'] || null, state['name'], state['type'] || ''); this.setValue(variable.getId()); } @@ -316,7 +316,8 @@ export class FieldVariable extends FieldDropdown { return null; } const newId = opt_newValue as string; - const variable = Variables.getVariable(this.sourceBlock_.workspace, newId); + const variable = + Variables.getVariable(this.getSourceBlock().workspace, newId); if (!variable) { console.warn( 'Variable id doesn\'t point to a real variable! ' + @@ -343,7 +344,7 @@ export class FieldVariable extends FieldDropdown { */ protected override doValueUpdate_(newId: AnyDuringMigration) { this.variable_ = - Variables.getVariable(this.sourceBlock_.workspace, newId as string); + Variables.getVariable(this.getSourceBlock().workspace, newId as string); super.doValueUpdate_(newId); } diff --git a/core/renderers/common/drawer.ts b/core/renderers/common/drawer.ts index 68653ae17..8751252da 100644 --- a/core/renderers/common/drawer.ts +++ b/core/renderers/common/drawer.ts @@ -295,7 +295,7 @@ export class Drawer { */ protected layoutField_(fieldInfo: Icon|Field) { const svgGroup = Types.isField(fieldInfo) ? - (fieldInfo as Field).field.getSvgRoot() : + (fieldInfo as Field).field.getSvgRoot()! : (fieldInfo as Icon).icon.iconGroup_!; // Never null in rendered case. const yPos = fieldInfo.centerline - fieldInfo.height / 2; diff --git a/core/tooltip.ts b/core/tooltip.ts index 9cece2c06..498490ee2 100644 --- a/core/tooltip.ts +++ b/core/tooltip.ts @@ -247,7 +247,7 @@ export function bindMouseEvents(element: Element) { * @param element SVG element onto which tooltip is bound. * @alias Blockly.Tooltip.unbindMouseEvents */ -export function unbindMouseEvents(element: Element) { +export function unbindMouseEvents(element: Element|null) { if (!element) { return; }