mirror of
https://github.com/google/blockly.git
synced 2026-01-11 10:57:07 +01:00
refactor: Make INavigable extend IFocusableNode. (#9033)
This commit is contained in:
@@ -1824,15 +1824,6 @@ export class BlockSvg
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this block can be navigated to via the keyboard.
|
||||
*
|
||||
* @returns True if this block is keyboard navigable, otherwise false.
|
||||
*/
|
||||
isNavigable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this block's class.
|
||||
*
|
||||
|
||||
@@ -1385,20 +1385,6 @@ export abstract class Field<T = any>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this field is accessible by keyboard navigation.
|
||||
*
|
||||
* @returns True if this field is keyboard accessible, otherwise false.
|
||||
*/
|
||||
isNavigable() {
|
||||
return (
|
||||
this.isClickable() &&
|
||||
this.isCurrentlyEditable() &&
|
||||
!(this.getSourceBlock()?.isSimpleReporter() && this.isFullBlockField()) &&
|
||||
this.getParentInput().isVisible()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this field's class.
|
||||
*
|
||||
|
||||
@@ -413,16 +413,6 @@ export class FlyoutButton
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this button is accessible through keyboard
|
||||
* navigation.
|
||||
*
|
||||
* @returns True if this button is keyboard accessible, otherwise false.
|
||||
*/
|
||||
isNavigable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this button's class.
|
||||
*
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
|
||||
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 {INavigable} from './interfaces/i_navigable.js';
|
||||
import {Rect} from './utils/rect.js';
|
||||
|
||||
@@ -12,7 +14,7 @@ import {Rect} from './utils/rect.js';
|
||||
* Representation of a gap between elements in a flyout.
|
||||
*/
|
||||
export class FlyoutSeparator
|
||||
implements IBoundedElement, INavigable<FlyoutSeparator>
|
||||
implements IBoundedElement, INavigable<FlyoutSeparator>, IFocusableNode
|
||||
{
|
||||
private x = 0;
|
||||
private y = 0;
|
||||
@@ -75,6 +77,27 @@ export class FlyoutSeparator
|
||||
getClass() {
|
||||
return FlyoutSeparator;
|
||||
}
|
||||
|
||||
/** See IFocusableNode.getFocusableElement. */
|
||||
getFocusableElement(): HTMLElement | SVGElement {
|
||||
throw new Error('Cannot be focused');
|
||||
}
|
||||
|
||||
/** See IFocusableNode.getFocusableTree. */
|
||||
getFocusableTree(): IFocusableTree {
|
||||
throw new Error('Cannot be focused');
|
||||
}
|
||||
|
||||
/** See IFocusableNode.onNodeFocus. */
|
||||
onNodeFocus(): void {}
|
||||
|
||||
/** See IFocusableNode.onNodeBlur. */
|
||||
onNodeBlur(): void {}
|
||||
|
||||
/** See IFocusableNode.canBeFocused. */
|
||||
canBeFocused(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,24 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type {IFocusableNode} from './i_focusable_node.js';
|
||||
|
||||
/**
|
||||
* Represents a UI element which can be navigated to using the keyboard.
|
||||
*/
|
||||
export interface INavigable<T> {
|
||||
/**
|
||||
* Returns whether or not this specific instance should be reachable via
|
||||
* keyboard navigation.
|
||||
*
|
||||
* Implementors should generally return true, unless there are circumstances
|
||||
* under which this item should be skipped while using keyboard navigation.
|
||||
* Common examples might include being disabled, invalid, readonly, or purely
|
||||
* a visual decoration. For example, while Fields are navigable, non-editable
|
||||
* fields return false, since they cannot be interacted with when focused.
|
||||
*
|
||||
* @returns True if this element should be included in keyboard navigation.
|
||||
*/
|
||||
isNavigable(): boolean;
|
||||
|
||||
export interface INavigable<T> extends IFocusableNode {
|
||||
/**
|
||||
* Returns the class of this instance.
|
||||
*
|
||||
|
||||
@@ -43,4 +43,18 @@ export interface INavigationPolicy<T> {
|
||||
* there is none.
|
||||
*/
|
||||
getPreviousSibling(current: T): INavigable<any> | null;
|
||||
|
||||
/**
|
||||
* Returns whether or not the given instance should be reachable via keyboard
|
||||
* navigation.
|
||||
*
|
||||
* Implementors should generally return true, unless there are circumstances
|
||||
* under which this item should be skipped while using keyboard navigation.
|
||||
* Common examples might include being disabled, invalid, readonly, or purely
|
||||
* a visual decoration. For example, while Fields are navigable, non-editable
|
||||
* fields return false, since they cannot be interacted with when focused.
|
||||
*
|
||||
* @returns True if this element should be included in keyboard navigation.
|
||||
*/
|
||||
isNavigable(current: T): boolean;
|
||||
}
|
||||
|
||||
@@ -114,4 +114,14 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
|
||||
}
|
||||
return block.outputConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given block can be navigated to.
|
||||
*
|
||||
* @param current The instance to check for navigability.
|
||||
* @returns True if the given block can be focused.
|
||||
*/
|
||||
isNavigable(current: BlockSvg): boolean {
|
||||
return current.canBeFocused();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,4 +166,14 @@ export class ConnectionNavigationPolicy
|
||||
}
|
||||
return block.outputConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given connection can be navigated to.
|
||||
*
|
||||
* @param current The instance to check for navigability.
|
||||
* @returns True if the given connection can be focused.
|
||||
*/
|
||||
isNavigable(current: RenderedConnection): boolean {
|
||||
return current.canBeFocused();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +88,23 @@ export class FieldNavigationPolicy implements INavigationPolicy<Field<any>> {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given field can be navigated to.
|
||||
*
|
||||
* @param current The instance to check for navigability.
|
||||
* @returns True if the given field can be focused and navigated to.
|
||||
*/
|
||||
isNavigable(current: Field<any>): boolean {
|
||||
return (
|
||||
current.canBeFocused() &&
|
||||
current.isClickable() &&
|
||||
current.isCurrentlyEditable() &&
|
||||
!(
|
||||
current.getSourceBlock()?.isSimpleReporter() &&
|
||||
current.isFullBlockField()
|
||||
) &&
|
||||
current.getParentInput().isVisible()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,14 @@ export class FlyoutButtonNavigationPolicy
|
||||
getPreviousSibling(_current: FlyoutButton): INavigable<unknown> | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given flyout button can be navigated to.
|
||||
*
|
||||
* @param current The instance to check for navigability.
|
||||
* @returns True if the given flyout button can be focused.
|
||||
*/
|
||||
isNavigable(current: FlyoutButton): boolean {
|
||||
return current.canBeFocused();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +88,14 @@ export class FlyoutNavigationPolicy<T> implements INavigationPolicy<T> {
|
||||
|
||||
return flyoutContents[index].getElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given flyout item can be navigated to.
|
||||
*
|
||||
* @param current The instance to check for navigability.
|
||||
* @returns True if the given flyout item can be focused.
|
||||
*/
|
||||
isNavigable(current: T): boolean {
|
||||
return this.policy.isNavigable(current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,14 @@ export class FlyoutSeparatorNavigationPolicy
|
||||
getPreviousSibling(_current: FlyoutSeparator): INavigable<unknown> | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given flyout separator can be navigated to.
|
||||
*
|
||||
* @param _current The instance to check for navigability.
|
||||
* @returns False.
|
||||
*/
|
||||
isNavigable(_current: FlyoutSeparator): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,14 @@ export class WorkspaceNavigationPolicy
|
||||
getPreviousSibling(_current: WorkspaceSvg): INavigable<unknown> | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given workspace can be navigated to.
|
||||
*
|
||||
* @param current The instance to check for navigability.
|
||||
* @returns True if the given workspace can be focused.
|
||||
*/
|
||||
isNavigable(current: WorkspaceSvg): boolean {
|
||||
return current.canBeFocused();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,9 @@ export class Navigator {
|
||||
const result = this.get(current)?.getFirstChild(current);
|
||||
if (!result) return null;
|
||||
// If the child isn't navigable, don't traverse into it; check its peers.
|
||||
if (!result.isNavigable()) return this.getNextSibling(result);
|
||||
if (!this.get(result)?.isNavigable(result)) {
|
||||
return this.getNextSibling(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -72,7 +74,7 @@ export class Navigator {
|
||||
getParent<T extends INavigable<T>>(current: T): INavigable<any> | null {
|
||||
const result = this.get(current)?.getParent(current);
|
||||
if (!result) return null;
|
||||
if (!result.isNavigable()) return this.getParent(result);
|
||||
if (!this.get(result)?.isNavigable(result)) return this.getParent(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -85,7 +87,9 @@ export class Navigator {
|
||||
getNextSibling<T extends INavigable<T>>(current: T): INavigable<any> | null {
|
||||
const result = this.get(current)?.getNextSibling(current);
|
||||
if (!result) return null;
|
||||
if (!result.isNavigable()) return this.getNextSibling(result);
|
||||
if (!this.get(result)?.isNavigable(result)) {
|
||||
return this.getNextSibling(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -100,7 +104,9 @@ export class Navigator {
|
||||
): INavigable<any> | null {
|
||||
const result = this.get(current)?.getPreviousSibling(current);
|
||||
if (!result) return null;
|
||||
if (!result.isNavigable()) return this.getPreviousSibling(result);
|
||||
if (!this.get(result)?.isNavigable(result)) {
|
||||
return this.getPreviousSibling(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,15 +665,6 @@ export class RenderedConnection
|
||||
| null as SVGElement | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this connection is keyboard-navigable.
|
||||
*
|
||||
* @returns True.
|
||||
*/
|
||||
isNavigable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this connection's class for keyboard navigation.
|
||||
*
|
||||
|
||||
@@ -2728,7 +2728,11 @@ export class WorkspaceSvg
|
||||
if (this.isFlyout && flyout) {
|
||||
for (const flyoutItem of flyout.getContents()) {
|
||||
const elem = flyoutItem.getElement();
|
||||
if (isFocusableNode(elem) && elem.getFocusableElement().id === id) {
|
||||
if (
|
||||
isFocusableNode(elem) &&
|
||||
elem.canBeFocused() &&
|
||||
elem.getFocusableElement().id === id
|
||||
) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
@@ -2817,15 +2821,6 @@ export class WorkspaceSvg
|
||||
return WorkspaceSvg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this workspace is keyboard-navigable.
|
||||
*
|
||||
* @returns True.
|
||||
*/
|
||||
isNavigable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object responsible for coordinating movement of focus between
|
||||
* items on this workspace in response to keyboard navigation commands.
|
||||
|
||||
Reference in New Issue
Block a user