Skip to content

Commit

Permalink
feat: support keyboard navigation of flyout buttons (#2200)
Browse files Browse the repository at this point in the history
* feat: support keyboard navigation of flyout buttons

* fix: linting errors and rebase
  • Loading branch information
mikeharv authored Apr 8, 2024
1 parent 3cded5a commit c2abe4d
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 13 deletions.
12 changes: 6 additions & 6 deletions plugins/keyboard-navigation/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 29 additions & 4 deletions plugins/keyboard-navigation/src/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ export class Navigation {

/**
* Sets the navigation state to flyout and moves the cursor to the first
* block in the flyout.
* block or button in the flyout.
* @param {!Blockly.WorkspaceSvg} workspace The workspace the flyout is on.
* @package
*/
Expand All @@ -500,9 +500,14 @@ export class Navigation {
}

if (flyout && flyout.getWorkspace()) {
const topBlocks = flyout.getWorkspace().getTopBlocks(true);
if (topBlocks.length > 0) {
const astNode = Blockly.ASTNode.createStackNode(topBlocks[0]);
const flyoutContents = flyout.getContents();
const firstFlyoutItem = flyoutContents[0];
if (firstFlyoutItem instanceof Blockly.FlyoutButton) {
const astNode = Blockly.ASTNode.createButtonNode(firstFlyoutItem);
this.getFlyoutCursor(workspace).setCurNode(astNode);
}
if (firstFlyoutItem instanceof Blockly.BlockSvg) {
const astNode = Blockly.ASTNode.createStackNode(firstFlyoutItem);
this.getFlyoutCursor(workspace).setCurNode(astNode);
}
}
Expand Down Expand Up @@ -1248,6 +1253,26 @@ export class Navigation {
return isHandled;
}

/**
* Triggers a flyout button's callback.
* @param {!Blockly.WorkspaceSvg} workspace The main workspace. The workspace
* containing a flyout with a button.
* @package
*/
triggerButtonCallback(workspace) {
const button = /** @type {!Blockly.FlyoutButton} */ (
this.getFlyoutCursor(workspace).getCurNode().getLocation()
);
const buttonCallback = workspace.flyoutButtonCallbacks.get(
button.callbackKey,
);
if (typeof buttonCallback === 'function') {
buttonCallback(button);
} else {
throw new Error('No callback function found for flyout button.');
}
}

/**
* Removes the change listeners on all registered workspaces.
* @package
Expand Down
21 changes: 20 additions & 1 deletion plugins/keyboard-navigation/src/navigation_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -504,12 +504,31 @@ export class NavigationController {
);
},
callback: (workspace) => {
let flyoutCursor;
let curNode;
let nodeType;

switch (this.navigation.getState(workspace)) {
case Constants.STATE.WORKSPACE:
this.navigation.handleEnterForWS(workspace);
return true;
case Constants.STATE.FLYOUT:
this.navigation.insertFromFlyout(workspace);
flyoutCursor = this.navigation.getFlyoutCursor(workspace);
if (!flyoutCursor) {
return false;
}
curNode = flyoutCursor.getCurNode();
nodeType = curNode.getType();

switch (nodeType) {
case Blockly.ASTNode.types.STACK:
this.navigation.insertFromFlyout(workspace);
break;
case Blockly.ASTNode.types.BUTTON:
this.navigation.triggerButtonCallback(workspace);
break;
}

return true;
default:
return false;
Expand Down
5 changes: 3 additions & 2 deletions plugins/keyboard-navigation/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
* @fileoverview Plugin test.
*/

import {createPlayground, toolboxCategories} from '@blockly/dev-tools';
import {createPlayground} from '@blockly/dev-tools';
import * as Blockly from 'blockly';
import {toolbox} from './toolbox';

import {LineCursor, NavigationController} from '../src';

Expand All @@ -31,7 +32,7 @@ document.addEventListener('DOMContentLoaded', function () {
controller = new NavigationController();
controller.init();
const defaultOptions = {
toolbox: toolboxCategories,
toolbox: toolbox,
};
createPlayground(
document.getElementById('root'),
Expand Down
218 changes: 218 additions & 0 deletions plugins/keyboard-navigation/test/toolbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview A custom toolbox for the plugin test.
*/

export const toolbox = {
kind: 'categoryToolbox',
contents: [
{
kind: 'category',
name: 'Logic',
categorystyle: 'logic_category',
contents: [
{
type: 'controls_if',
kind: 'block',
},
{
type: 'logic_compare',
kind: 'block',
fields: {
OP: 'EQ',
},
},
{
type: 'logic_operation',
kind: 'block',
fields: {
OP: 'AND',
},
},
],
},
{
kind: 'category',
name: 'Loops',
categorystyle: 'loop_category',
contents: [
{
type: 'controls_repeat_ext',
kind: 'block',
inputs: {
TIMES: {
shadow: {
type: 'math_number',
fields: {
NUM: 10,
},
},
},
},
},
{
type: 'controls_repeat',
kind: 'block',
enabled: false,
fields: {
TIMES: 10,
},
},
{
type: 'controls_whileUntil',
kind: 'block',
fields: {
MODE: 'WHILE',
},
},
{
type: 'controls_for',
kind: 'block',
fields: {
VAR: {
name: 'i',
},
},
inputs: {
FROM: {
shadow: {
type: 'math_number',
fields: {
NUM: 1,
},
},
},
TO: {
shadow: {
type: 'math_number',
fields: {
NUM: 10,
},
},
},
BY: {
shadow: {
type: 'math_number',
fields: {
NUM: 1,
},
},
},
},
},
{
type: 'controls_forEach',
kind: 'block',
fields: {
VAR: {
name: 'j',
},
},
},
{
type: 'controls_flow_statements',
kind: 'block',
enabled: false,
fields: {
FLOW: 'BREAK',
},
},
],
},
{
kind: 'sep',
},
{
kind: 'category',
name: 'Variables',
custom: 'VARIABLE',
categorystyle: 'variable_category',
},
{
kind: 'category',
name: 'Buttons and Blocks',
categorystyle: 'loop_category',
contents: [
{
type: 'controls_repeat',
kind: 'block',
fields: {
TIMES: 10,
},
},
{
kind: 'BUTTON',
text: 'Randomize Button Style',
callbackkey: 'setRandomStyle',
},
{
kind: 'BUTTON',
text: 'Randomize Button Style',
callbackkey: 'setRandomStyle',
},
{
type: 'controls_repeat',
kind: 'block',
fields: {
TIMES: 10,
},
},
{
kind: 'BUTTON',
text: 'Randomize Button Style',
callbackkey: 'setRandomStyle',
},
],
},
{
kind: 'sep',
},
{
kind: 'category',
name: 'Nested Categories',
contents: [
{
kind: 'category',
name: 'sub-category 1',
contents: [
{
kind: 'BUTTON',
text: 'Randomize Button Style',
callbackkey: 'setRandomStyle',
},
{
type: 'logic_boolean',
kind: 'block',
fields: {
BOOL: 'TRUE',
},
},
],
},
{
kind: 'category',
name: 'sub-category 2',
contents: [
{
type: 'logic_boolean',
kind: 'block',
fields: {
BOOL: 'FALSE',
},
},
{
kind: 'BUTTON',
text: 'Randomize Button Style',
callbackkey: 'setRandomStyle',
},
],
},
],
},
],
};

0 comments on commit c2abe4d

Please sign in to comment.