diff --git a/core/block.ts b/core/block.ts index af44facda5d..c50b5ae1c3a 100644 --- a/core/block.ts +++ b/core/block.ts @@ -962,16 +962,42 @@ export class Block { } /** - * @returns True if this block is a value block with a single editable field. + * @returns True if this block is a value block with a full block field. + * @param mustBeEditable Whether the evaluated field must be editable. * @internal */ - isSimpleReporter(): boolean { - if (!this.outputConnection) return false; + isSimpleReporter( + mustBeFullBlock: boolean = false, + mustBeEditable: boolean = false, + ): boolean { + return ( + this.getSingletonFullBlockField(mustBeFullBlock, mustBeEditable) !== null + ); + } - for (const input of this.inputList) { - if (input.connection || input.fieldRow.length > 1) return false; - } - return true; + /** + * Determies and returns the only field of this block, or null if there isn't + * one and this block can't be considered a simple reporter. Null will also be + * returned if the singleton block doesn't match additional criteria, if set, + * such as being full-block or editable. + * + * @param mustBeFullBlock Whether the returned field must be 'full-block'. + * @param mustBeEditable Whether the returned field must be editable. + * @returns The only full-block, maybe editable field of this block, or null. + * @internal + */ + getSingletonFullBlockField( + mustBeFullBlock: boolean, + mustBeEditable: boolean, + ): Field | null { + if (!this.outputConnection) return null; + for (const input of this.inputList) if (input.connection) return null; + const matchingFields = Array.from(this.getFields()).filter((field) => { + if (mustBeFullBlock && !field.isFullBlockField()) return false; + if (mustBeEditable && !field.isCurrentlyEditable()) return false; + return true; + }); + return matchingFields.length === 1 ? matchingFields[0] : null; } /** diff --git a/core/block_svg.ts b/core/block_svg.ts index d312f418a43..50f76e50fc1 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -233,10 +233,7 @@ export class BlockSvg * @internal */ recomputeAriaLabel() { - if (this.isSimpleReporter()) { - const field = Array.from(this.getFields())[0]; - if (field.isFullBlockField() && field.isCurrentlyEditable()) return; - } + if (this.isSimpleReporter(true, true)) return; aria.setState( this.getFocusableElement(), @@ -1964,13 +1961,8 @@ export class BlockSvg /** See IFocusableNode.getFocusableElement. */ getFocusableElement(): HTMLElement | SVGElement { - if (this.isSimpleReporter()) { - const field = Array.from(this.getFields())[0]; - if (field && field.isFullBlockField() && field.isCurrentlyEditable()) { - return field.getFocusableElement(); - } - } - return this.pathObject.svgPath; + const singletonField = this.getSingletonFullBlockField(true, true); + return singletonField?.getFocusableElement() ?? this.pathObject.svgPath; } /** See IFocusableNode.getFocusableTree. */ diff --git a/core/contextmenu_items.ts b/core/contextmenu_items.ts index 001a3c58e25..764758322b7 100644 --- a/core/contextmenu_items.ts +++ b/core/contextmenu_items.ts @@ -26,12 +26,6 @@ import {Coordinate} from './utils/coordinate.js'; import * as svgMath from './utils/svg_math.js'; import type {WorkspaceSvg} from './workspace_svg.js'; -function isFullBlockField(block?: BlockSvg) { - if (!block || !block.isSimpleReporter()) return false; - const firstField = block.getFields().next().value; - return firstField?.isFullBlockField(); -} - /** * Option to undo previous action. */ @@ -377,7 +371,7 @@ export function registerComment() { // Either block already has a comment so let us remove it, // or the block isn't just one full-block field block, which // shouldn't be allowed to have comments as there's no way to read them. - (block.hasIcon(CommentIcon.TYPE) || !isFullBlockField(block)) + (block.hasIcon(CommentIcon.TYPE) || !block.isSimpleReporter(true)) ) { return 'enabled'; } diff --git a/core/field.ts b/core/field.ts index ccbd3442275..3bb3d5caf40 100644 --- a/core/field.ts +++ b/core/field.ts @@ -330,12 +330,9 @@ export abstract class Field this.initModel(); this.applyColour(); - const id = - this.isFullBlockField() && - this.isCurrentlyEditable() && - this.sourceBlock_?.isSimpleReporter() - ? idGenerator.getNextUniqueId() - : `${this.sourceBlock_?.id}_field_${idGenerator.getNextUniqueId()}`; + const id = this.sourceBlock_?.isSimpleReporter(true, true) + ? idGenerator.getNextUniqueId() + : `${this.sourceBlock_?.id}_field_${idGenerator.getNextUniqueId()}`; this.fieldGroup_.setAttribute('id', id); } diff --git a/core/keyboard_nav/field_navigation_policy.ts b/core/keyboard_nav/field_navigation_policy.ts index f9df406c22c..fb332a1cf85 100644 --- a/core/keyboard_nav/field_navigation_policy.ts +++ b/core/keyboard_nav/field_navigation_policy.ts @@ -65,10 +65,7 @@ export class FieldNavigationPolicy implements INavigationPolicy> { current.canBeFocused() && current.isVisible() && (current.isClickable() || current.isCurrentlyEditable()) && - !( - current.getSourceBlock()?.isSimpleReporter() && - current.isFullBlockField() - ) && + !current.getSourceBlock()?.isSimpleReporter(true, true) && current.getParentInput().isVisible() ); }