Files
blockly/core/toolbox/toolbox_item.ts
Ben Henning 4074cee31b feat!: Make everything ISelectable focusable (#9004)
* feat!: Make bubbles, comments, and icons focusable

* feat!: Make ISelectable and ICopyable focusable.

* feat: Consolidate selection calls.

Now everything is based on focus with selection only being used as a
proxy.

* feat: Invert responsibility for setSelected().

Now setSelected() is only for quasi-external use.

* feat: Push up shadow check to getters.

Needed new common-level helper.

* chore: Lint fixes.

* feat!: Allow IFocusableNode to disable focus.

* chore: post-merge lint fixes

* fix: Fix tests + text bubble focusing.

This fixed then regressed a circular dependency causing the node and
advanced compilation steps to fail. This investigation is ongoing.

* fix: Clean up & fix imports.

This ensures the node and advanced compilation test steps now pass.

* fix: Lint fixes + revert commented out logic.

* chore: Remove unnecessary cast.

Addresses reviewer comment.

* fix: Some issues and a bunch of clean-ups.

This addresses a bunch of review comments, and fixes selecting workspace
comments.

* chore: Lint fix.

* fix: Remove unnecessary shadow consideration.

* chore: Revert import.

* chore: Some doc updates & added a warn statement.
2025-05-09 08:16:14 -07:00

182 lines
4.6 KiB
TypeScript

/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* An item in the toolbox.
*
* @class
*/
// Former goog.module ID: Blockly.ToolboxItem
import type {ICollapsibleToolboxItem} from '../interfaces/i_collapsible_toolbox_item.js';
import type {IFocusableTree} from '../interfaces/i_focusable_tree.js';
import type {IToolbox} from '../interfaces/i_toolbox.js';
import type {IToolboxItem} from '../interfaces/i_toolbox_item.js';
import * as idGenerator from '../utils/idgenerator.js';
import type * as toolbox from '../utils/toolbox.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
/**
* Class for an item in the toolbox.
*/
export class ToolboxItem implements IToolboxItem {
protected id_: string;
protected parent_: ICollapsibleToolboxItem | null;
protected level_: number;
protected toolboxItemDef_: toolbox.ToolboxItemInfo | null;
protected workspace_: WorkspaceSvg;
/** The toolbox this category belongs to. */
protected readonly parentToolbox_: IToolbox;
/**
* @param toolboxItemDef The JSON defining the toolbox item.
* @param parentToolbox The toolbox that holds the toolbox item.
* @param opt_parent The parent toolbox item or null if the category does not
* have a parent.
*/
constructor(
toolboxItemDef: toolbox.ToolboxItemInfo,
parentToolbox: IToolbox,
opt_parent?: ICollapsibleToolboxItem,
) {
/** The ID for the category. */
this.id_ =
(toolboxItemDef as AnyDuringMigration)['toolboxitemid'] ||
idGenerator.getNextUniqueId();
/** The parent of the category. */
this.parent_ = opt_parent || null;
/** The level that the category is nested at. */
this.level_ = this.parent_ ? this.parent_.getLevel() + 1 : 0;
/** The JSON definition of the toolbox item. */
this.toolboxItemDef_ = toolboxItemDef;
this.parentToolbox_ = parentToolbox;
/** The workspace of the parent toolbox. */
this.workspace_ = this.parentToolbox_.getWorkspace();
}
/**
* Initializes the toolbox item.
* This includes creating the DOM and updating the state of any items based
* on the info object.
*/
init() {}
// No-op by default.
/**
* Gets the div for the toolbox item.
*
* @returns The div for the toolbox item.
*/
getDiv(): Element | null {
return null;
}
/**
* Gets the HTML element that is clickable.
* The parent toolbox element receives clicks. The parent toolbox will add an
* ID to this element so it can pass the onClick event to the correct
* toolboxItem.
*
* @returns The HTML element that receives clicks, or null if this item should
* not receive clicks.
*/
getClickTarget(): Element | null {
return null;
}
/**
* Gets a unique identifier for this toolbox item.
*
* @returns The ID for the toolbox item.
*/
getId(): string {
return this.id_;
}
/**
* Gets the parent if the toolbox item is nested.
*
* @returns The parent toolbox item, or null if this toolbox item is not
* nested.
*/
getParent(): ICollapsibleToolboxItem | null {
return null;
}
/**
* Gets the nested level of the category.
*
* @returns The nested level of the category.
* @internal
*/
getLevel(): number {
return this.level_;
}
/**
* Whether the toolbox item is selectable.
*
* @returns True if the toolbox item can be selected.
*/
isSelectable(): boolean {
return false;
}
/**
* Whether the toolbox item is collapsible.
*
* @returns True if the toolbox item is collapsible.
*/
isCollapsible(): boolean {
return false;
}
/** Dispose of this toolbox item. No-op by default. */
dispose() {}
/**
* Sets whether the category is visible or not.
* For a category to be visible its parent category must also be expanded.
*
* @param _isVisible True if category should be visible.
*/
setVisible_(_isVisible: boolean) {}
/** See IFocusableNode.getFocusableElement. */
getFocusableElement(): HTMLElement | SVGElement {
const div = this.getDiv();
if (!div) {
throw Error('Trying to access toolbox item before DOM is initialized.');
}
if (!(div instanceof HTMLElement)) {
throw Error('Toolbox item div is unexpectedly not an HTML element.');
}
return div as HTMLElement;
}
/** See IFocusableNode.getFocusableTree. */
getFocusableTree(): IFocusableTree {
return this.parentToolbox_;
}
/** See IFocusableNode.onNodeFocus. */
onNodeFocus(): void {}
/** See IFocusableNode.onNodeBlur. */
onNodeBlur(): void {}
/** See IFocusableNode.canBeFocused. */
canBeFocused(): boolean {
return true;
}
}
// nop by default