mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +01:00
feat: Remove most block tree support. (#9412)
Also, use regions for identifiying toolbox, workspace, and flyout.
This commit is contained in:
@@ -300,42 +300,18 @@ export class BlockSvg
|
||||
private computeAriaRole() {
|
||||
if (this.isSimpleReporter()) {
|
||||
aria.setRole(this.pathObject.svgPath, aria.Role.BUTTON);
|
||||
} else {
|
||||
// This isn't read out by VoiceOver and it will read in the wrong place
|
||||
// as a duplicate in ChromeVox due to the other changes in this branch.
|
||||
// aria.setState(
|
||||
// this.pathObject.svgPath,
|
||||
// aria.State.ROLEDESCRIPTION,
|
||||
// 'block',
|
||||
// );
|
||||
} else if (this.workspace.isFlyout) {
|
||||
aria.setRole(this.pathObject.svgPath, aria.Role.TREEITEM);
|
||||
}
|
||||
}
|
||||
|
||||
collectSiblingBlocks(surroundParent: BlockSvg | null): BlockSvg[] {
|
||||
// NOTE TO DEVELOPERS: it's very important that these are NOT sorted. The
|
||||
// returned list needs to be relatively stable for consistent block indexes
|
||||
// read out to users via screen readers.
|
||||
if (surroundParent) {
|
||||
// Start from the first sibling and iterate in navigation order.
|
||||
const firstSibling: BlockSvg = surroundParent.getChildren(false)[0];
|
||||
const siblings: BlockSvg[] = [firstSibling];
|
||||
let nextSibling: BlockSvg | null = firstSibling;
|
||||
while ((nextSibling = nextSibling?.getNextBlock())) {
|
||||
siblings.push(nextSibling);
|
||||
}
|
||||
return siblings;
|
||||
} else {
|
||||
// For top-level blocks, simply return those from the workspace.
|
||||
return this.workspace.getTopBlocks(false);
|
||||
aria.setState(
|
||||
this.pathObject.svgPath,
|
||||
aria.State.ROLEDESCRIPTION,
|
||||
'block',
|
||||
);
|
||||
aria.setRole(this.pathObject.svgPath, aria.Role.FIGURE);
|
||||
}
|
||||
}
|
||||
|
||||
computeLevelInWorkspace(): number {
|
||||
const surroundParent = this.getSurroundParent();
|
||||
return surroundParent ? surroundParent.computeLevelInWorkspace() + 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and initialize the SVG representation of the block.
|
||||
* May be called more than once.
|
||||
|
||||
@@ -154,9 +154,6 @@ export class Toolbox
|
||||
this.setVisible(true);
|
||||
this.flyout.init(workspace);
|
||||
|
||||
aria.setRole(this.HtmlDiv, aria.Role.TREE);
|
||||
aria.setState(this.HtmlDiv, aria.State.LABEL, Msg['TOOLBOX_ARIA_LABEL']);
|
||||
|
||||
this.render(this.toolboxDef_);
|
||||
const themeManager = workspace.getThemeManager();
|
||||
themeManager.subscribe(
|
||||
@@ -208,6 +205,12 @@ export class Toolbox
|
||||
toolboxContainer.setAttribute('layout', this.isHorizontal() ? 'h' : 'v');
|
||||
dom.addClass(toolboxContainer, 'blocklyToolbox');
|
||||
toolboxContainer.setAttribute('dir', this.RTL ? 'RTL' : 'LTR');
|
||||
aria.setRole(toolboxContainer, aria.Role.REGION);
|
||||
aria.setState(
|
||||
toolboxContainer,
|
||||
aria.State.LABEL,
|
||||
Msg['TOOLBOX_ARIA_LABEL'],
|
||||
);
|
||||
return toolboxContainer;
|
||||
}
|
||||
|
||||
@@ -222,6 +225,7 @@ export class Toolbox
|
||||
if (this.isHorizontal()) {
|
||||
contentsContainer.style.flexDirection = 'row';
|
||||
}
|
||||
aria.setRole(contentsContainer, aria.Role.TREE);
|
||||
return contentsContainer;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ export enum Role {
|
||||
TEXTBOX = 'textbox',
|
||||
COMBOBOX = 'combobox',
|
||||
SPINBUTTON = 'spinbutton',
|
||||
REGION = 'region',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,6 @@ import {WorkspaceComment} from './comments/workspace_comment.js';
|
||||
import * as common from './common.js';
|
||||
import {ComponentManager} from './component_manager.js';
|
||||
import {ConnectionDB} from './connection_db.js';
|
||||
import {ConnectionType} from './connection_type.js';
|
||||
import * as ContextMenu from './contextmenu.js';
|
||||
import {
|
||||
ContextMenuOption,
|
||||
@@ -763,19 +762,15 @@ export class WorkspaceSvg
|
||||
});
|
||||
|
||||
let ariaLabel = null;
|
||||
if (injectionDiv) {
|
||||
ariaLabel = Msg['WORKSPACE_ARIA_LABEL'];
|
||||
} else if (this.isFlyout) {
|
||||
if (this.isFlyout) {
|
||||
ariaLabel = 'Flyout';
|
||||
} else if (this.isMutator) {
|
||||
ariaLabel = 'Mutator';
|
||||
ariaLabel = 'Mutator Workspace';
|
||||
} else {
|
||||
// This case can happen in some test scenarios.
|
||||
// TODO: Figure out when this can happen in non-test scenarios (if ever).
|
||||
ariaLabel = 'Workspace';
|
||||
ariaLabel = Msg['WORKSPACE_ARIA_LABEL'];
|
||||
}
|
||||
aria.setRole(this.svgGroup_, aria.Role.REGION);
|
||||
aria.setState(this.svgGroup_, aria.State.LABEL, ariaLabel);
|
||||
aria.setRole(this.svgGroup_, aria.Role.TREE);
|
||||
|
||||
// Note that a <g> alone does not receive mouse events--it must have a
|
||||
// valid target inside it. If no background class is specified, as in the
|
||||
@@ -803,7 +798,10 @@ export class WorkspaceSvg
|
||||
this.svgBlockCanvas_ = this.layerManager.getBlockLayer();
|
||||
this.svgBubbleCanvas_ = this.layerManager.getBubbleLayer();
|
||||
|
||||
if (!this.isFlyout) {
|
||||
if (this.isFlyout) {
|
||||
// Use the block canvas as the primary tree parent for flyout blocks.
|
||||
aria.setRole(this.svgBlockCanvas_, aria.Role.TREE);
|
||||
} else {
|
||||
browserEvents.conditionalBind(
|
||||
this.svgGroup_,
|
||||
'pointerdown',
|
||||
@@ -2959,61 +2957,9 @@ export class WorkspaceSvg
|
||||
aria.setState(treeItemElem, aria.State.POSINSET, index + 1);
|
||||
aria.setState(treeItemElem, aria.State.SETSIZE, focusableItems.length);
|
||||
aria.setState(treeItemElem, aria.State.LEVEL, 1); // They are always top-level.
|
||||
if (item instanceof BlockSvg) {
|
||||
item
|
||||
.getChildren(false)
|
||||
.forEach((child) =>
|
||||
this.recomputeAriaTreeItemDetailsRecursively(child),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO: Do this efficiently (probably incrementally).
|
||||
this.getTopBlocks(false).forEach((block) =>
|
||||
this.recomputeAriaTreeItemDetailsRecursively(block),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private recomputeAriaTreeItemDetailsRecursively(block: BlockSvg) {
|
||||
const elem = block.getFocusableElement();
|
||||
const connection = block.currentConnectionCandidate;
|
||||
let childPosition: number;
|
||||
let parentsChildCount: number;
|
||||
let hierarchyDepth: number;
|
||||
if (connection) {
|
||||
// If the block is being inserted into a new location, the position is hypothetical.
|
||||
// TODO: Figure out how to deal with output connections.
|
||||
let surroundParent: BlockSvg | null;
|
||||
let siblingBlocks: BlockSvg[];
|
||||
if (connection.type === ConnectionType.INPUT_VALUE) {
|
||||
surroundParent = connection.sourceBlock_;
|
||||
siblingBlocks = block.collectSiblingBlocks(surroundParent);
|
||||
// The block is being added as a child since it's input.
|
||||
// TODO: Figure out how to compute the correct position.
|
||||
childPosition = 0;
|
||||
} else {
|
||||
surroundParent = connection.sourceBlock_.getSurroundParent();
|
||||
siblingBlocks = block.collectSiblingBlocks(surroundParent);
|
||||
// The block is being added after the connected block.
|
||||
childPosition = siblingBlocks.indexOf(connection.sourceBlock_) + 1;
|
||||
}
|
||||
parentsChildCount = siblingBlocks.length + 1;
|
||||
hierarchyDepth = surroundParent?.computeLevelInWorkspace() ?? 0;
|
||||
} else {
|
||||
const surroundParent = block.getSurroundParent();
|
||||
const siblingBlocks = block.collectSiblingBlocks(surroundParent);
|
||||
childPosition = siblingBlocks.indexOf(block);
|
||||
parentsChildCount = siblingBlocks.length;
|
||||
hierarchyDepth = block.computeLevelInWorkspace();
|
||||
}
|
||||
aria.setState(elem, aria.State.POSINSET, childPosition + 1);
|
||||
aria.setState(elem, aria.State.SETSIZE, parentsChildCount);
|
||||
aria.setState(elem, aria.State.LEVEL, hierarchyDepth + 1);
|
||||
block
|
||||
.getChildren(false)
|
||||
.forEach((child) => this.recomputeAriaTreeItemDetailsRecursively(child));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user