diff --git a/core/interfaces/i_toolbox_item.js b/core/interfaces/i_toolbox_item.js index 87bc0d8dd..b6e302d50 100644 --- a/core/interfaces/i_toolbox_item.js +++ b/core/interfaces/i_toolbox_item.js @@ -113,6 +113,15 @@ Blockly.ISelectableToolboxItem.prototype.getContents; */ Blockly.ISelectableToolboxItem.prototype.setSelected; +/** + * 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. + * @return {!Element} The html element that receives clicks. + * @public + */ +Blockly.ISelectableToolboxItem.prototype.getClickTarget; + /** * Handles when the toolbox item is clicked. * @param {!Event} _e Click event to handle. diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index ac7c34d23..8736acbda 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -133,7 +133,14 @@ Blockly.navigation.focusToolbox_ = function() { Blockly.navigation.markAtCursor_(); } if (!toolbox.getSelectedItem()) { - toolbox.selectItemByPosition(0); + // Find the first item that is selectable. + var toolboxItems = toolbox.getToolboxItems(); + for (var i = 0, toolboxItem; (toolboxItem = toolboxItems[i]); i++) { + if (toolboxItem.isSelectable()) { + toolbox.selectItemByPosition(i); + break; + } + } } } }; diff --git a/core/toolbox/category.js b/core/toolbox/category.js index 201bbba66..fc5a5e37f 100644 --- a/core/toolbox/category.js +++ b/core/toolbox/category.js @@ -82,6 +82,13 @@ Blockly.ToolboxCategory = function(categoryDef, toolbox, opt_parent) { */ this.iconDom_ = null; + /** + * The html element for the toolbox label. + * @type {?Element} + * @protected + */ + this.labelDom_ = null; + /** * All the css class names that are used to create a category. * @type {!Blockly.ToolboxCategory.CssConfig} @@ -222,7 +229,6 @@ Blockly.ToolboxCategory.prototype.createDom_ = function() { Blockly.utils.aria.State.LEVEL, this.level_); this.rowDiv_ = this.createRowContainer_(); - this.rowDiv_.setAttribute('id', this.id_); this.rowDiv_.style.pointerEvents = 'auto'; this.htmlDiv_.appendChild(this.rowDiv_); @@ -234,10 +240,10 @@ Blockly.ToolboxCategory.prototype.createDom_ = function() { Blockly.utils.aria.setRole(this.iconDom_, Blockly.utils.aria.Role.PRESENTATION); this.rowContents_.appendChild(this.iconDom_); - var labelDom = this.createLabelDom_(this.name_); - this.rowContents_.appendChild(labelDom); + this.labelDom_ = this.createLabelDom_(this.name_); + this.rowContents_.appendChild(this.labelDom_); Blockly.utils.aria.setState(/** @type {!Element} */ (this.htmlDiv_), - Blockly.utils.aria.State.LABELLEDBY, labelDom.getAttribute('id')); + Blockly.utils.aria.State.LABELLEDBY, this.labelDom_.getAttribute('id')); this.addColourBorder_(this.colour_); @@ -383,6 +389,17 @@ Blockly.ToolboxCategory.prototype.getColourfromStyle_ = function(styleName) { return ''; }; +/** + * 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. + * @return {!Element} The html element that receives clicks. + * @public + */ +Blockly.ToolboxCategory.prototype.getClickTarget = function() { + return /** @type {!Element} */(this.rowDiv_); +}; + /** * Parses the colour on the category. * @param {number|string} colourValue HSV hue value (0 to 360), #RRGGBB string, diff --git a/core/toolbox/collapsible_category.js b/core/toolbox/collapsible_category.js index 6621d4731..a56d56aa1 100644 --- a/core/toolbox/collapsible_category.js +++ b/core/toolbox/collapsible_category.js @@ -200,6 +200,9 @@ Blockly.CollapsibleToolboxCategory.prototype.createSubCategoriesDom_ = function( newCategory.init(); var newCategoryDiv = newCategory.getDiv(); contentsContainer.appendChild(newCategoryDiv); + if (newCategory.getClickTarget) { + newCategory.getClickTarget().setAttribute('id', newCategory.getId()); + } } return contentsContainer; }; diff --git a/core/toolbox/toolbox.js b/core/toolbox/toolbox.js index 91eb03433..db713bf51 100644 --- a/core/toolbox/toolbox.js +++ b/core/toolbox/toolbox.js @@ -304,6 +304,9 @@ Blockly.Toolbox.prototype.onKeyDown_ = function(e) { handled = false; break; } + if (!handled && this.selectedItem_ && this.selectedItem_.onKeyDown) { + handled = this.selectedItem_.onKeyDown(e); + } if (handled) { e.preventDefault(); @@ -414,6 +417,11 @@ Blockly.Toolbox.prototype.createToolboxItem_ = function(toolboxItemDef, fragment if (toolboxItemDom) { fragment.appendChild(toolboxItemDom); } + // Adds the id to the html element that can receive a click. + // This is used in onClick_ to find the toolboxItem that was clicked. + if (toolboxItem.getClickTarget) { + toolboxItem.getClickTarget().setAttribute('id', toolboxItem.getId()); + } } }; diff --git a/core/utils/toolbox.js b/core/utils/toolbox.js index 4d7e58ef8..563e6eb53 100644 --- a/core/utils/toolbox.js +++ b/core/utils/toolbox.js @@ -342,7 +342,7 @@ Blockly.utils.toolbox.xmlToJsonArray_ = function(toolboxDef) { // Store the xml for a block if (tagName == 'BLOCK') { obj['blockxml'] = child; - } else if (tagName == 'CATEGORY') { + } else if (child.childNodes && child.childNodes.length > 0) { // Get the contents of a category obj['contents'] = Blockly.utils.toolbox.xmlToJsonArray_(child); }