Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import {IContextMenu} from './interfaces/i_contextmenu.js';
import type {ICopyable} from './interfaces/i_copyable.js';
import {IDeletable} from './interfaces/i_deletable.js';
import type {IDragStrategy, IDraggable} from './interfaces/i_draggable.js';
import type {IFocusableNode} from './interfaces/i_focusable_node.js';
import type {IFocusableTree} from './interfaces/i_focusable_tree.js';
import {IIcon} from './interfaces/i_icon.js';
import * as internalConstants from './internal_constants.js';
import {MarkerManager} from './marker_manager.js';
Expand Down Expand Up @@ -76,7 +78,8 @@ export class BlockSvg
IContextMenu,
ICopyable<BlockCopyData>,
IDraggable,
IDeletable
IDeletable,
IFocusableNode
{
/**
* Constant for identifying rows that are to be rendered inline.
Expand Down Expand Up @@ -210,6 +213,7 @@ export class BlockSvg

// Expose this block's ID on its top-level SVG group.
this.svgGroup.setAttribute('data-id', this.id);
svgPath.id = this.id;

this.doInit_();
}
Expand Down Expand Up @@ -1827,4 +1831,26 @@ export class BlockSvg
);
}
}

/** See IFocusableNode.getFocusableElement. */
getFocusableElement(): HTMLElement | SVGElement {
return this.pathObject.svgPath;
}

/** See IFocusableNode.getFocusableTree. */
getFocusableTree(): IFocusableTree {
return this.workspace;
}

/** See IFocusableNode.onNodeFocus. */
onNodeFocus(): void {
common.setSelected(this);
}

/** See IFocusableNode.onNodeBlur. */
onNodeBlur(): void {
if (common.getSelected() === this) {
common.setSelected(null);
}
}
}
12 changes: 2 additions & 10 deletions core/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,8 +463,8 @@ input[type=number] {
}

.blocklyMenuSeparator {
background-color: #ccc;
height: 1px;
background-color: #ccc;
height: 1px;
border: 0;
margin-left: 4px;
margin-right: 4px;
Expand Down Expand Up @@ -494,12 +494,4 @@ input[type=number] {
cursor: grabbing;
}

.blocklyActiveFocus {
outline-color: #2ae;
outline-width: 2px;
}
.blocklyPassiveFocus {
outline-color: #3fdfff;
outline-width: 1.5px;
}
`;
69 changes: 68 additions & 1 deletion core/flyout_base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ import * as eventUtils from './events/utils.js';
import {FlyoutItem} from './flyout_item.js';
import {FlyoutMetricsManager} from './flyout_metrics_manager.js';
import {FlyoutSeparator, SeparatorAxis} from './flyout_separator.js';
import {getFocusManager} from './focus_manager.js';
import {IAutoHideable} from './interfaces/i_autohideable.js';
import type {IFlyout} from './interfaces/i_flyout.js';
import type {IFlyoutInflater} from './interfaces/i_flyout_inflater.js';
import {IFocusableNode} from './interfaces/i_focusable_node.js';
import {IFocusableTree} from './interfaces/i_focusable_tree.js';
import type {Options} from './options.js';
import * as registry from './registry.js';
import * as renderManagement from './render_management.js';
Expand All @@ -43,7 +46,7 @@ import {WorkspaceSvg} from './workspace_svg.js';
*/
export abstract class Flyout
extends DeleteArea
implements IAutoHideable, IFlyout
implements IAutoHideable, IFlyout, IFocusableNode
{
/**
* Position the flyout.
Expand Down Expand Up @@ -303,6 +306,7 @@ export abstract class Flyout
// hide/show code will set up proper visibility and size later.
this.svgGroup_ = dom.createSvgElement(tagName, {
'class': 'blocklyFlyout',
'tabindex': '0',
});
this.svgGroup_.style.display = 'none';
this.svgBackground_ = dom.createSvgElement(
Expand All @@ -317,6 +321,9 @@ export abstract class Flyout
this.workspace_
.getThemeManager()
.subscribe(this.svgBackground_, 'flyoutOpacity', 'fill-opacity');

getFocusManager().registerTree(this);

return this.svgGroup_;
}

Expand Down Expand Up @@ -398,6 +405,7 @@ export abstract class Flyout
if (this.svgGroup_) {
dom.removeNode(this.svgGroup_);
}
getFocusManager().unregisterTree(this);
}

/**
Expand Down Expand Up @@ -961,4 +969,63 @@ export abstract class Flyout

return null;
}

/** See IFocusableNode.getFocusableElement. */
getFocusableElement(): HTMLElement | SVGElement {
if (!this.svgGroup_) throw new Error('Flyout DOM is not yet created.');
return this.svgGroup_;
}

/** See IFocusableNode.getFocusableTree. */
getFocusableTree(): IFocusableTree {
return this;
}

/** See IFocusableNode.onNodeFocus. */
onNodeFocus(): void {}

/** See IFocusableNode.onNodeBlur. */
onNodeBlur(): void {}

/** See IFocusableTree.getRootFocusableNode. */
getRootFocusableNode(): IFocusableNode {
return this;
}

/** See IFocusableTree.getRestoredFocusableNode. */
getRestoredFocusableNode(
_previousNode: IFocusableNode | null,
): IFocusableNode | null {
return null;
}

/** See IFocusableTree.getNestedTrees. */
getNestedTrees(): Array<IFocusableTree> {
return [this.workspace_];
}

/** See IFocusableTree.lookUpFocusableNode. */
lookUpFocusableNode(_id: string): IFocusableNode | null {
// No focusable node needs to be returned since the flyout's subtree is a
// workspace that will manage its own focusable state.
return null;
}

/** See IFocusableTree.onTreeFocus. */
onTreeFocus(
_node: IFocusableNode,
_previousTree: IFocusableTree | null,
): void {}

/** See IFocusableTree.onTreeBlur. */
onTreeBlur(nextTree: IFocusableTree | null): void {
const toolbox = this.targetWorkspace.getToolbox();
// If focus is moving to either the toolbox or the flyout's workspace, do
// not close the flyout. For anything else, do close it since the flyout is
// no longer focused.
if (toolbox && nextTree === toolbox) return;
if (nextTree == this.workspace_) return;
if (toolbox) toolbox.clearSelection();
this.autoHide(false);
}
}
26 changes: 24 additions & 2 deletions core/flyout_button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import type {IASTNodeLocationSvg} from './blockly.js';
import * as browserEvents from './browser_events.js';
import * as Css from './css.js';
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
import type {IFocusableNode} from './interfaces/i_focusable_node.js';
import type {IFocusableTree} from './interfaces/i_focusable_tree.js';
import type {IRenderedElement} from './interfaces/i_rendered_element.js';
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
Expand All @@ -29,7 +31,11 @@ import type {WorkspaceSvg} from './workspace_svg.js';
* Class for a button or label in the flyout.
*/
export class FlyoutButton
implements IASTNodeLocationSvg, IBoundedElement, IRenderedElement
implements
IASTNodeLocationSvg,
IBoundedElement,
IRenderedElement,
IFocusableNode
{
/** The horizontal margin around the text in the button. */
static TEXT_MARGIN_X = 5;
Expand Down Expand Up @@ -107,7 +113,7 @@ export class FlyoutButton

this.svgGroup = dom.createSvgElement(
Svg.G,
{'class': cssClass},
{'class': cssClass, 'tabindex': '-1'},
this.workspace.getCanvas(),
);

Expand Down Expand Up @@ -389,6 +395,22 @@ export class FlyoutButton
getSvgRoot() {
return this.svgGroup;
}

/** See IFocusableNode.getFocusableElement. */
getFocusableElement(): HTMLElement | SVGElement {
return this.svgGroup;
}

/** See IFocusableNode.getFocusableTree. */
getFocusableTree(): IFocusableTree {
return this.workspace;
}

/** See IFocusableNode.onNodeFocus. */
onNodeFocus(): void {}

/** See IFocusableNode.onNodeBlur. */
onNodeBlur(): void {}
}

/** CSS for buttons and labels. See css.js for use. */
Expand Down
5 changes: 0 additions & 5 deletions core/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ import * as common from './common.js';
import * as Css from './css.js';
import * as dropDownDiv from './dropdowndiv.js';
import {Grid} from './grid.js';
import {Msg} from './msg.js';
import {Options} from './options.js';
import {ScrollbarPair} from './scrollbar_pair.js';
import {ShortcutRegistry} from './shortcut_registry.js';
import * as Tooltip from './tooltip.js';
import * as Touch from './touch.js';
import * as aria from './utils/aria.js';
import * as dom from './utils/dom.js';
import {Svg} from './utils/svg.js';
import * as WidgetDiv from './widgetdiv.js';
Expand Down Expand Up @@ -56,8 +54,6 @@ export function inject(
if (opt_options?.rtl) {
dom.addClass(subContainer, 'blocklyRTL');
}
subContainer.tabIndex = 0;
aria.setState(subContainer, aria.State.LABEL, Msg['WORKSPACE_ARIA_LABEL']);

containerElement!.appendChild(subContainer);
const svg = createDom(subContainer, options);
Expand Down Expand Up @@ -126,7 +122,6 @@ function createDom(container: HTMLElement, options: Options): SVGElement {
'xmlns:xlink': dom.XLINK_NS,
'version': '1.1',
'class': 'blocklySvg',
'tabindex': '0',
},
container,
);
Expand Down
3 changes: 2 additions & 1 deletion core/interfaces/i_flyout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import type {Coordinate} from '../utils/coordinate.js';
import type {Svg} from '../utils/svg.js';
import type {FlyoutDefinition} from '../utils/toolbox.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import {IFocusableTree} from './i_focusable_tree.js';
import type {IRegistrable} from './i_registrable.js';

/**
* Interface for a flyout.
*/
export interface IFlyout extends IRegistrable {
export interface IFlyout extends IRegistrable, IFocusableTree {
/** Whether the flyout is laid out horizontally or not. */
horizontalLayout: boolean;

Expand Down
3 changes: 2 additions & 1 deletion core/interfaces/i_toolbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
import type {ToolboxInfo} from '../utils/toolbox.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import type {IFlyout} from './i_flyout.js';
import type {IFocusableTree} from './i_focusable_tree.js';
import type {IRegistrable} from './i_registrable.js';
import type {IToolboxItem} from './i_toolbox_item.js';

/**
* Interface for a toolbox.
*/
export interface IToolbox extends IRegistrable {
export interface IToolbox extends IRegistrable, IFocusableTree {
/** Initializes the toolbox. */
init(): void;

Expand Down
4 changes: 3 additions & 1 deletion core/interfaces/i_toolbox_item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

// Former goog.module ID: Blockly.IToolboxItem

import type {IFocusableNode} from './i_focusable_node.js';

/**
* Interface for an item in the toolbox.
*/
export interface IToolboxItem {
export interface IToolboxItem extends IFocusableNode {
/**
* Initializes the toolbox item.
* This includes creating the DOM and updating the state of any items based
Expand Down
2 changes: 1 addition & 1 deletion core/renderers/common/path_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class PathObject implements IPathObject {
/** The primary path of the block. */
this.svgPath = dom.createSvgElement(
Svg.PATH,
{'class': 'blocklyPath'},
{'class': 'blocklyPath', 'tabindex': '-1'},
this.svgRoot,
);

Expand Down
2 changes: 2 additions & 0 deletions core/toolbox/category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ export class ToolboxCategory
*/
protected createContainer_(): HTMLDivElement {
const container = document.createElement('div');
container.tabIndex = -1;
container.id = this.getId();
const className = this.cssConfig_['container'];
if (className) {
dom.addClass(container, className);
Expand Down
2 changes: 2 additions & 0 deletions core/toolbox/separator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export class ToolboxSeparator extends ToolboxItem {
*/
protected createDom_(): HTMLDivElement {
const container = document.createElement('div');
container.tabIndex = -1;
container.id = this.getId();
const className = this.cssConfig_['container'];
if (className) {
dom.addClass(container, className);
Expand Down
Loading