Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
18 changes: 13 additions & 5 deletions core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,22 @@ export class BlockSvg
(newParent as BlockSvg).getSvgRoot().appendChild(svgRoot);
} else if (oldParent) {
// If we are losing a parent, we want to move our DOM element to the
// root of the workspace.
const draggingBlock = this.workspace
// root of the workspace. Try to insert it before any top-level
// block being dragged, but note that blocks can have the
// blocklyDragging class even if they're not top blocks (especially
// at start and end of a drag).
const draggingBlockElement = this.workspace
.getCanvas()
.querySelector('.blocklyDragging');
if (draggingBlock) {
this.workspace.getCanvas().insertBefore(svgRoot, draggingBlock);
const draggingParentElement = draggingBlockElement?.parentElement as
| SVGElement
| null
| undefined;
const canvas = this.workspace.getCanvas();
if (draggingParentElement === canvas) {
canvas.insertBefore(svgRoot, draggingBlockElement);
} else {
this.workspace.getCanvas().appendChild(svgRoot);
canvas.appendChild(svgRoot);
}
this.translate(oldXY.x, oldXY.y);
}
Expand Down
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;
}
`;
18 changes: 9 additions & 9 deletions core/dragging/block_drag_strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export class BlockDragStrategy implements IDragStrategy {
const currCandidate = this.connectionCandidate;
const newCandidate = this.getConnectionCandidate(draggingBlock, delta);
if (!newCandidate) {
this.connectionPreviewer!.hidePreview();
this.connectionPreviewer?.hidePreview();
this.connectionCandidate = null;
return;
}
Expand All @@ -254,7 +254,7 @@ export class BlockDragStrategy implements IDragStrategy {
local.type === ConnectionType.OUTPUT_VALUE ||
local.type === ConnectionType.PREVIOUS_STATEMENT;
const neighbourIsConnectedToRealBlock =
neighbour.isConnected() && !neighbour.targetBlock()!.isInsertionMarker();
neighbour.isConnected() && !neighbour.targetBlock()?.isInsertionMarker();
if (
localIsOutputOrPrevious &&
neighbourIsConnectedToRealBlock &&
Expand All @@ -264,14 +264,14 @@ export class BlockDragStrategy implements IDragStrategy {
local.type,
)
) {
this.connectionPreviewer!.previewReplacement(
this.connectionPreviewer?.previewReplacement(
local,
neighbour,
neighbour.targetBlock()!,
);
return;
}
this.connectionPreviewer!.previewConnection(local, neighbour);
this.connectionPreviewer?.previewConnection(local, neighbour);
}

/**
Expand Down Expand Up @@ -385,7 +385,7 @@ export class BlockDragStrategy implements IDragStrategy {
dom.stopTextWidthCache();

blockAnimation.disconnectUiStop();
this.connectionPreviewer!.hidePreview();
this.connectionPreviewer?.hidePreview();

if (!this.block.isDeadOrDying() && this.dragging) {
// These are expensive and don't need to be done if we're deleting, or
Expand Down Expand Up @@ -413,7 +413,7 @@ export class BlockDragStrategy implements IDragStrategy {

// Must dispose after connections are applied to not break the dynamic
// connections plugin. See #7859
this.connectionPreviewer!.dispose();
this.connectionPreviewer?.dispose();
this.workspace.setResizesEnabled(true);
eventUtils.setGroup(newGroup);
}
Expand Down Expand Up @@ -445,6 +445,9 @@ export class BlockDragStrategy implements IDragStrategy {
return;
}

this.connectionPreviewer?.hidePreview();
this.connectionCandidate = null;

this.startChildConn?.connect(this.block.nextConnection);
if (this.startParentConn) {
switch (this.startParentConn.type) {
Expand All @@ -471,9 +474,6 @@ export class BlockDragStrategy implements IDragStrategy {
this.startChildConn = null;
this.startParentConn = null;

this.connectionPreviewer!.hidePreview();
this.connectionCandidate = null;

this.block.setDragging(false);
this.dragging = false;
}
Expand Down
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);
}
}
31 changes: 29 additions & 2 deletions core/flyout_button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ 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 {idGenerator} from './utils.js';
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
import * as parsing from './utils/parsing.js';
Expand All @@ -29,7 +32,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 @@ -68,6 +75,9 @@ export class FlyoutButton
*/
cursorSvg: SVGElement | null = null;

/** The unique ID for this FlyoutButton. */
private id: string;

/**
* @param workspace The workspace in which to place this button.
* @param targetWorkspace The flyout's target workspace.
Expand Down Expand Up @@ -105,9 +115,10 @@ export class FlyoutButton
cssClass += ' ' + this.cssClass;
}

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

Expand Down Expand Up @@ -389,6 +400,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
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
7 changes: 5 additions & 2 deletions core/keyboard_nav/line_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,12 @@ export class LineCursor extends Marker {
if (!curNode) {
return null;
}
const newNode = this.getPreviousNodeImpl(
const newNode = this.getPreviousNode(
curNode,
this.validLineNode.bind(this),
true,
);

if (newNode) {
this.setCurNode(newNode);
}
Expand All @@ -168,9 +170,10 @@ export class LineCursor extends Marker {
if (!curNode) {
return null;
}
const newNode = this.getPreviousNodeImpl(
const newNode = this.getPreviousNode(
curNode,
this.validInLineNode.bind(this),
true,
);

if (newNode) {
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
Loading