mirror of
https://github.com/google/blockly.git
synced 2026-01-28 11:10:10 +01:00
Blockly Factory: New Selector UI (#579)
* generate block selector with checkboxes working click handler updated block exporter view and controller to work with new UI select a block by clicking on the option--not just checkbox renamed selectorWorkspace and fixed checkbox selecting bug adding used blocks works added and cleaned up css, removed extra exporter controller function, nit comment simplified code * style * does not clear selected blocks upon tab switch * added tooltips to buttons, reworded some buttons * remove console.log statement and clarify this.selected in blockoption * removed console log & nit comment
This commit is contained in:
@@ -305,8 +305,9 @@ AppController.prototype.onTab = function() {
|
||||
this.selectedTab == AppController.WORKSPACE_FACTORY;
|
||||
|
||||
if (this.selectedTab == AppController.EXPORTER) {
|
||||
// Update toolbox to reflect current block library.
|
||||
this.exporter.updateToolbox();
|
||||
// Show container of exporter.
|
||||
FactoryUtils.show('blockLibraryExporter');
|
||||
FactoryUtils.hide('workspaceFactoryContent');
|
||||
|
||||
// Need accurate state in order to know which blocks are used in workspace
|
||||
// factory.
|
||||
@@ -316,13 +317,12 @@ AppController.prototype.onTab = function() {
|
||||
var usedBlockTypes = this.workspaceFactoryController.getAllUsedBlockTypes();
|
||||
this.exporter.setUsedBlockTypes(usedBlockTypes);
|
||||
|
||||
// Update exporter's block selector to reflect current block library.
|
||||
this.exporter.updateSelector();
|
||||
|
||||
// Update the preview to reflect any changes made to the blocks.
|
||||
this.exporter.updatePreview();
|
||||
|
||||
// Show container of exporter.
|
||||
FactoryUtils.show('blockLibraryExporter');
|
||||
FactoryUtils.hide('workspaceFactoryContent');
|
||||
|
||||
} else if (this.selectedTab == AppController.BLOCK_FACTORY) {
|
||||
// Hide container of exporter.
|
||||
FactoryUtils.hide('blockLibraryExporter');
|
||||
@@ -368,19 +368,22 @@ AppController.prototype.assignExporterClickHandlers = function() {
|
||||
|
||||
document.getElementById('dropdown_addAllUsed').addEventListener('click',
|
||||
function() {
|
||||
self.exporter.addUsedBlocksToWorkspace();
|
||||
self.exporter.selectUsedBlocks();
|
||||
self.exporter.updatePreview();
|
||||
document.getElementById('dropdownDiv_setBlocks').classList.remove("show");
|
||||
});
|
||||
|
||||
document.getElementById('dropdown_clearSelected').addEventListener('click',
|
||||
function() {
|
||||
self.exporter.clearSelectedBlocks();
|
||||
self.exporter.updatePreview();
|
||||
document.getElementById('dropdownDiv_setBlocks').classList.remove("show");
|
||||
});
|
||||
|
||||
document.getElementById('dropdown_addAllFromLib').addEventListener('click',
|
||||
function() {
|
||||
self.exporter.addAllBlocksToWorkspace();
|
||||
self.exporter.selectAllBlocks();
|
||||
self.exporter.updatePreview();
|
||||
document.getElementById('dropdownDiv_setBlocks').classList.remove("show");
|
||||
});
|
||||
|
||||
@@ -436,8 +439,6 @@ AppController.prototype.assignExporterChangeListeners = function() {
|
||||
function(e) {
|
||||
self.exporter.updatePreview();
|
||||
});
|
||||
|
||||
self.exporter.addChangeListenersToSelectorWorkspace();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -557,6 +558,28 @@ AppController.prototype.initializeBlocklyStorage = function() {
|
||||
BlockFactory.disableEnableLink();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle resizing of elements.
|
||||
*/
|
||||
AppController.prototype.onresize = function(event) {
|
||||
// Handle resizing of Block Factory elements.
|
||||
var expandList = [
|
||||
document.getElementById('blockly'),
|
||||
document.getElementById('blocklyMask'),
|
||||
document.getElementById('preview'),
|
||||
document.getElementById('languagePre'),
|
||||
document.getElementById('languageTA'),
|
||||
document.getElementById('generatorPre')
|
||||
];
|
||||
for (var i = 0, expand; expand = expandList[i]; i++) {
|
||||
expand.style.width = (expand.parentNode.offsetWidth - 2) + 'px';
|
||||
expand.style.height = (expand.parentNode.offsetHeight - 2) + 'px';
|
||||
}
|
||||
|
||||
// Handle resize of Exporter block options.
|
||||
this.exporter.view.centerPreviewBlocks();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Blockly and layout. Called on page load.
|
||||
*/
|
||||
@@ -571,24 +594,11 @@ AppController.prototype.init = function() {
|
||||
this.assignLibraryClickHandlers();
|
||||
this.assignBlockFactoryClickHandlers();
|
||||
|
||||
// Handle resizing of Block Factory elements.
|
||||
var expandList = [
|
||||
document.getElementById('blockly'),
|
||||
document.getElementById('blocklyMask'),
|
||||
document.getElementById('preview'),
|
||||
document.getElementById('languagePre'),
|
||||
document.getElementById('languageTA'),
|
||||
document.getElementById('generatorPre')
|
||||
];
|
||||
|
||||
var onresize = function(e) {
|
||||
for (var i = 0, expand; expand = expandList[i]; i++) {
|
||||
expand.style.width = (expand.parentNode.offsetWidth - 2) + 'px';
|
||||
expand.style.height = (expand.parentNode.offsetHeight - 2) + 'px';
|
||||
}
|
||||
};
|
||||
onresize();
|
||||
window.addEventListener('resize', onresize);
|
||||
this.onresize();
|
||||
self = this;
|
||||
window.addEventListener('resize', function() {
|
||||
self.onresize();
|
||||
});
|
||||
|
||||
// Inject Block Factory Main Workspace.
|
||||
var toolbox = document.getElementById('blockfactory_toolbox');
|
||||
|
||||
@@ -43,16 +43,19 @@ goog.require('goog.dom.xml');
|
||||
* @param {!BlockLibrary.Storage} blockLibStorage - Block Library Storage.
|
||||
*/
|
||||
BlockExporterController = function(blockLibStorage) {
|
||||
// BlockLibrary.Storage object containing user's saved blocks
|
||||
// BlockLibrary.Storage object containing user's saved blocks.
|
||||
this.blockLibStorage = blockLibStorage;
|
||||
// Utils for generating code to export
|
||||
// Utils for generating code to export.
|
||||
this.tools = new BlockExporterTools();
|
||||
// View provides the selector workspace and export settings UI.
|
||||
this.view = new BlockExporterView(
|
||||
//Xml representation of the toolbox
|
||||
this.tools.generateToolboxFromLibrary(this.blockLibStorage));
|
||||
// Array to hold the block types used in workspace factory.
|
||||
this.usedBlockTypes = [];
|
||||
// The ID of the block selector, a div element that will be populated with the
|
||||
// block options.
|
||||
this.selectorID = 'blockSelector';
|
||||
// Map of block types stored in block library to their corresponding Block
|
||||
// Option objects.
|
||||
this.blockOptions = this.tools.createBlockSelectorFromLib(
|
||||
this.blockLibStorage, this.selectorID);
|
||||
// View provides the block selector and export settings UI.
|
||||
this.view = new BlockExporterView(this.blockOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,35 +75,17 @@ BlockExporterController.prototype.setBlockLibStorage =
|
||||
* @return {!BlockLibraryStorage} blockLibStorage - Block Library Storage object
|
||||
* that stores the blocks.
|
||||
*/
|
||||
BlockExporterController.prototype.getBlockLibStorage =
|
||||
function(blockLibStorage) {
|
||||
BlockExporterController.prototype.getBlockLibStorage = function() {
|
||||
return this.blockLibStorage;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected block types.
|
||||
* @private
|
||||
*
|
||||
* @return {!Array.<string>} Types of blocks in workspace.
|
||||
*/
|
||||
BlockExporterController.prototype.getSelectedBlockTypes_ = function() {
|
||||
var selectedBlocks = this.view.getSelectedBlocks();
|
||||
var blockTypes = [];
|
||||
for (var i = 0, block; block = selectedBlocks[i]; i++) {
|
||||
blockTypes.push(block.type);
|
||||
}
|
||||
return blockTypes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get selected blocks from selector workspace, pulls info from the Export
|
||||
* Get selected blocks from block selector, pulls info from the Export
|
||||
* Settings form in Block Exporter, and downloads code accordingly.
|
||||
*
|
||||
* TODO(quachtina96): allow export as zip.
|
||||
*/
|
||||
BlockExporterController.prototype.export = function() {
|
||||
// Get selected blocks' information.
|
||||
var blockTypes = this.getSelectedBlockTypes_();
|
||||
var blockTypes = this.view.getSelectedBlockTypes();
|
||||
var blockXmlMap = this.blockLibStorage.getBlockXmlMap(blockTypes);
|
||||
|
||||
// Pull block definition(s) settings from the Export Settings form.
|
||||
@@ -153,157 +138,48 @@ BlockExporterController.prototype.export = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the Exporter's toolbox with either the given toolbox xml or toolbox
|
||||
* xml generated from blocks stored in block library.
|
||||
*
|
||||
* @param {Element} opt_toolboxXml - Xml to define toolbox of the selector
|
||||
* workspace.
|
||||
* Update the Exporter's block selector with block options generated from blocks
|
||||
* stored in block library.
|
||||
*/
|
||||
BlockExporterController.prototype.updateToolbox = function(opt_toolboxXml) {
|
||||
// Use given xml or xml generated from updated block library.
|
||||
var updatedToolbox = opt_toolboxXml ||
|
||||
this.tools.generateToolboxFromLibrary(this.blockLibStorage);
|
||||
BlockExporterController.prototype.updateSelector = function() {
|
||||
// Get previously selected block types.
|
||||
var oldSelectedTypes = this.view.getSelectedBlockTypes();
|
||||
|
||||
// Update the view's toolbox.
|
||||
this.view.setToolbox(updatedToolbox);
|
||||
// Generate options from block library and assign to view.
|
||||
this.blockOptions = this.tools.createBlockSelectorFromLib(
|
||||
this.blockLibStorage, this.selectorID);
|
||||
this.addBlockOptionSelectHandlers();
|
||||
this.view.setBlockOptions(this.blockOptions);
|
||||
|
||||
// Render the toolbox in the selector workspace.
|
||||
this.view.renderToolbox(updatedToolbox);
|
||||
|
||||
// Do not try to disable any selected blocks deleted from the block library.
|
||||
// Instead, deselect them.
|
||||
var selectedBlocks = this.view.getSelectedBlocks();
|
||||
var updatedSelectedBlocks = [];
|
||||
for (var i = 0, selectedBlock; selectedBlock = selectedBlocks[i]; i++) {
|
||||
if (this.blockLibStorage[selectedBlock.type]) {
|
||||
updatedSelectedBlocks.push(selectedBlock);
|
||||
} else {
|
||||
this.view.removeBlock(selectedBlock);
|
||||
// Select all previously selected blocks.
|
||||
for (var i = 0, blockType; blockType = oldSelectedTypes[i]; i++) {
|
||||
if (this.blockOptions[blockType]) {
|
||||
this.view.select(blockType);
|
||||
}
|
||||
}
|
||||
// Disable any selected blocks.
|
||||
var selectedBlockTypes = this.getSelectedBlockTypes_();
|
||||
for (var i = 0, blockType; blockType = selectedBlockTypes[i]; i++) {
|
||||
this.setBlockEnabled(blockType, false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable or Disable block in selector workspace's toolbox.
|
||||
*
|
||||
* @param {!string} blockType - Type of block to disable or enable.
|
||||
* @param {!boolean} enable - True to enable the block, false to disable block.
|
||||
*/
|
||||
BlockExporterController.prototype.setBlockEnabled =
|
||||
function(blockType, enable) {
|
||||
// Get toolbox xml, category, and block elements.
|
||||
var toolboxXml = this.view.toolbox;
|
||||
var category = goog.dom.xml.selectSingleNode(toolboxXml,
|
||||
'//category[@name="' + blockType + '"]');
|
||||
var block = goog.dom.getFirstElementChild(category);
|
||||
// Enable block.
|
||||
goog.dom.xml.setAttributes(block, {disabled: !enable});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add change listeners to the exporter's selector workspace.
|
||||
*/
|
||||
BlockExporterController.prototype.addChangeListenersToSelectorWorkspace
|
||||
= function() {
|
||||
// Assign the BlockExporterController to 'self' to be called in the change
|
||||
// listeners. This keeps it in scope--otherwise, 'this' in the change
|
||||
// listeners refers to the wrong thing.
|
||||
var self = this;
|
||||
var selector = this.view.selectorWorkspace;
|
||||
selector.addChangeListener(
|
||||
function(event) {
|
||||
self.onSelectBlockForExport_(event);
|
||||
});
|
||||
selector.addChangeListener(
|
||||
function(event) {
|
||||
self.onDeselectBlockForExport_(event);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function for when a user selects a block for export in selector
|
||||
* workspace. Disables selected block so that the user only exports one
|
||||
* copy of starter code per block. Attached to the blockly create event in block
|
||||
* factory expansion's init.
|
||||
* @private
|
||||
*
|
||||
* @param {!Blockly.Events} event - The fired Blockly event.
|
||||
*/
|
||||
BlockExporterController.prototype.onSelectBlockForExport_ = function(event) {
|
||||
// The user created a block in selector workspace.
|
||||
if (event.type == Blockly.Events.CREATE) {
|
||||
// Get type of block created.
|
||||
var block = this.view.selectorWorkspace.getBlockById(event.blockId);
|
||||
var blockType = block.type;
|
||||
// Disable the selected block. Users can only export one copy of starter
|
||||
// code per block.
|
||||
this.setBlockEnabled(blockType, false);
|
||||
// Show currently selected blocks in helper text.
|
||||
this.view.listSelectedBlocks(this.getSelectedBlockTypes_());
|
||||
this.updatePreview();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function for when a user deselects a block in selector
|
||||
* workspace by deleting it. Re-enables block so that the user may select it for
|
||||
* export
|
||||
* @private
|
||||
*
|
||||
* @param {!Blockly.Events} event - The fired Blockly event.
|
||||
*/
|
||||
BlockExporterController.prototype.onDeselectBlockForExport_ = function(event) {
|
||||
// The user deleted a block in selector workspace.
|
||||
if (event.type == Blockly.Events.DELETE) {
|
||||
// Get type of block created.
|
||||
var deletedBlockXml = event.oldXml;
|
||||
var blockType = deletedBlockXml.getAttribute('type');
|
||||
// Do not try to enable any blocks deleted from the block library.
|
||||
if (this.blockLibStorage.has(blockType)) {
|
||||
// Enable the deselected block.
|
||||
this.setBlockEnabled(blockType, true);
|
||||
}
|
||||
// Show currently selected blocks in helper text.
|
||||
this.view.listSelectedBlocks(this.getSelectedBlockTypes_());
|
||||
this.updatePreview();
|
||||
}
|
||||
this.view.listSelectedBlocks();
|
||||
};
|
||||
|
||||
/**
|
||||
* Tied to the 'Clear Selected Blocks' button in the Block Exporter.
|
||||
* Deselects all blocks on the selector workspace by deleting them and updating
|
||||
* text accordingly.
|
||||
* Deselects all blocks in the selector and updates text accordingly.
|
||||
*/
|
||||
BlockExporterController.prototype.clearSelectedBlocks = function() {
|
||||
// Clear selector workspace.
|
||||
this.view.clearSelectorWorkspace();
|
||||
this.view.deselectAllBlocks();
|
||||
this.view.listSelectedBlocks();
|
||||
};
|
||||
|
||||
/**
|
||||
* Tied to the 'Add All Stored Blocks' button in the Block Exporter.
|
||||
* Adds all blocks stored in block library to the selector workspace.
|
||||
* Tied to the 'All Stored' button in the Block Exporter 'Select' dropdown.
|
||||
* Selects all blocks stored in block library for export.
|
||||
*/
|
||||
BlockExporterController.prototype.addAllBlocksToWorkspace = function() {
|
||||
// Clear selector workspace.
|
||||
this.view.clearSelectorWorkspace();
|
||||
|
||||
// Add and evaluate all blocks' definitions.
|
||||
BlockExporterController.prototype.selectAllBlocks = function() {
|
||||
var allBlockTypes = this.blockLibStorage.getBlockTypes();
|
||||
var blockXmlMap = this.blockLibStorage.getBlockXmlMap(allBlockTypes);
|
||||
this.tools.addBlockDefinitions(blockXmlMap);
|
||||
|
||||
// For every block, render in selector workspace.
|
||||
for (var i = 0, blockType; blockType = allBlockTypes[i]; i++) {
|
||||
this.view.addBlock(blockType);
|
||||
this.view.select(blockType);
|
||||
}
|
||||
|
||||
// Clean up workspace.
|
||||
this.view.cleanUpSelectorWorkspace();
|
||||
this.view.listSelectedBlocks();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -311,17 +187,52 @@ BlockExporterController.prototype.addAllBlocksToWorkspace = function() {
|
||||
*
|
||||
* @return {Element} Xml for a category to be used in toolbox.
|
||||
*/
|
||||
|
||||
BlockExporterController.prototype.getBlockLibCategory = function() {
|
||||
return this.tools.generateCategoryFromBlockLib(this.blockLibStorage);
|
||||
};
|
||||
|
||||
/**
|
||||
* Tied to the 'Add All Stored Blocks' button in the Block Exporter.
|
||||
* Adds all blocks stored in block library to the selector workspace.
|
||||
* Add select handlers to each block option to update the view and the selected
|
||||
* blocks accordingly.
|
||||
*/
|
||||
BlockExporterController.prototype.addUsedBlocksToWorkspace = function() {
|
||||
// Clear selector workspace.
|
||||
this.view.clearSelectorWorkspace();
|
||||
BlockExporterController.prototype.addBlockOptionSelectHandlers = function() {
|
||||
var self = this;
|
||||
|
||||
// Click handler for a block option. Toggles whether or not it's selected and
|
||||
// updates helper text accordingly.
|
||||
var updateSelectedBlockTypes_ = function(blockOption) {
|
||||
// Toggle selected.
|
||||
blockOption.setSelected(!blockOption.isSelected());
|
||||
|
||||
// Show currently selected blocks in helper text.
|
||||
self.view.listSelectedBlocks();
|
||||
};
|
||||
|
||||
// Returns a block option select handler.
|
||||
var makeBlockOptionSelectHandler_ = function(blockOption) {
|
||||
return function() {
|
||||
updateSelectedBlockTypes_(blockOption);
|
||||
self.updatePreview();
|
||||
};
|
||||
};
|
||||
|
||||
// Assign a click handler to each block option.
|
||||
for (var blockType in this.blockOptions) {
|
||||
var blockOption = this.blockOptions[blockType];
|
||||
// Use an additional closure to correctly assign the tab callback.
|
||||
blockOption.dom.addEventListener(
|
||||
'click', makeBlockOptionSelectHandler_(blockOption));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tied to the 'All Used' button in the Block Exporter's 'Select' button.
|
||||
* Selects all blocks stored in block library and used in workspace factory.
|
||||
*/
|
||||
BlockExporterController.prototype.selectUsedBlocks = function() {
|
||||
// Deselect all blocks.
|
||||
this.view.deselectAllBlocks();
|
||||
|
||||
// Get list of block types that are in block library and used in workspace
|
||||
// factory.
|
||||
@@ -338,20 +249,14 @@ BlockExporterController.prototype.addUsedBlocksToWorkspace = function() {
|
||||
}
|
||||
}
|
||||
|
||||
// Add and evaluate the shared blocks' definitions.
|
||||
var blockXmlMap = this.blockLibStorage.getBlockXmlMap(sharedBlockTypes);
|
||||
this.tools.addBlockDefinitions(blockXmlMap);
|
||||
|
||||
// For every block, render in selector workspace.
|
||||
// Select each shared block type.
|
||||
for (var i = 0, blockType; blockType = sharedBlockTypes[i]; i++) {
|
||||
this.view.addBlock(blockType);
|
||||
this.view.select(blockType);
|
||||
}
|
||||
|
||||
// Clean up workspace.
|
||||
this.view.cleanUpSelectorWorkspace();
|
||||
this.view.listSelectedBlocks();
|
||||
|
||||
if (unstoredCustomBlockTypes.length > 0){
|
||||
// Warn user to import block definitions and generator code for blocks
|
||||
// Warn user to import block defifnitions and generator code for blocks
|
||||
// not in their Block Library nor Blockly's standard library.
|
||||
var blockTypesText = unstoredCustomBlockTypes.join(', ');
|
||||
var customWarning = 'Custom blocks used in workspace factory but not ' +
|
||||
@@ -393,7 +298,7 @@ BlockExporterController.prototype.updatePreview = function() {
|
||||
* corresponding xml element.
|
||||
*/
|
||||
BlockExporterController.prototype.getSelectedBlockXmlMap = function() {
|
||||
var blockTypes = this.getSelectedBlockTypes_();
|
||||
var blockTypes = this.view.getSelectedBlockTypes();
|
||||
return this.blockLibStorage.getBlockXmlMap(blockTypes);
|
||||
};
|
||||
|
||||
@@ -426,3 +331,4 @@ BlockExporterController.prototype.getGeneratorStubsOfSelected = function() {
|
||||
var language = document.getElementById('exportLanguage').value;
|
||||
return this.tools.getGeneratorCode(blockXmlMap, language);
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
goog.provide('BlockExporterTools');
|
||||
|
||||
goog.require('FactoryUtils');
|
||||
goog.require('BlockOption');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.xml');
|
||||
|
||||
@@ -230,3 +231,46 @@ BlockExporterTools.prototype.generateCategoryFromBlockLib =
|
||||
|
||||
return FactoryUtils.generateCategoryXml(blocks,'Block Library');
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate selector dom from block library storage. For each block in the
|
||||
* library, it has a block option, which consists of a checkbox, a label,
|
||||
* and a fixed size preview workspace.
|
||||
*
|
||||
* @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object.
|
||||
* @param {!string} blockSelectorID - ID of the div element that will contain
|
||||
* the block options.
|
||||
* @return {!Object} Map of block type to Block Option object.
|
||||
*/
|
||||
BlockExporterTools.prototype.createBlockSelectorFromLib =
|
||||
function(blockLibStorage, blockSelectorID) {
|
||||
// Object mapping each stored block type to XML.
|
||||
var allBlockTypes = blockLibStorage.getBlockTypes();
|
||||
var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes);
|
||||
|
||||
// Define the custom blocks in order to be able to create instances of
|
||||
// them in the exporter workspace.
|
||||
this.addBlockDefinitions(blockXmlMap);
|
||||
|
||||
var blockSelector = goog.dom.getElement(blockSelectorID);
|
||||
// Clear the block selector.
|
||||
goog.dom.removeChildren(blockSelector);
|
||||
|
||||
// Append each block option's dom to the selector.
|
||||
var blockOptions = Object.create(null);
|
||||
for (var blockType in blockXmlMap) {
|
||||
// Get preview block's xml.
|
||||
var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
|
||||
var previewBlockXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);
|
||||
|
||||
// Create block option, inject block into preview workspace, and append
|
||||
// option to block selector.
|
||||
var blockOpt = new BlockOption(blockSelector, blockType, previewBlockXml);
|
||||
blockOpt.createDom();
|
||||
goog.dom.appendChild(blockSelector, blockOpt.dom);
|
||||
blockOpt.showPreviewBlock();
|
||||
blockOptions[blockType] = blockOpt;
|
||||
}
|
||||
return blockOptions;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Javascript for the Block Exporter View class. Takes care of
|
||||
* generating the selector workspace through which users select blocks to
|
||||
* export.
|
||||
* @fileoverview Javascript for the Block Exporter View class. Reads from and
|
||||
* manages a block selector through which users select blocks to export.
|
||||
*
|
||||
* @author quachtina96 (Tina Quach)
|
||||
*/
|
||||
@@ -30,55 +29,29 @@
|
||||
|
||||
goog.provide('BlockExporterView');
|
||||
|
||||
goog.require('BlockExporterTools');
|
||||
goog.require('BlockOption');
|
||||
goog.require('goog.dom');
|
||||
|
||||
/**
|
||||
* BlockExporter View Class
|
||||
* @constructor
|
||||
*
|
||||
* @param {Element} toolbox - Xml for the toolbox of the selector workspace.
|
||||
* @param {!Object} blockOptions - Map of block types to BlockOption objects.
|
||||
*/
|
||||
BlockExporterView = function(selectorToolbox) {
|
||||
// Xml representation of the toolbox
|
||||
if (selectorToolbox.hasChildNodes) {
|
||||
this.toolbox = selectorToolbox;
|
||||
} else {
|
||||
// Toolbox is empty. Append dummy category to toolbox because toolbox
|
||||
// cannot switch between category and flyout-only mode after injection.
|
||||
var categoryElement = goog.dom.createDom('category');
|
||||
categoryElement.setAttribute('name', 'Next Saved Block');
|
||||
selectorToolbox.appendChild(categoryElement);
|
||||
this.toolbox = selectorToolbox;
|
||||
}
|
||||
// Workspace users use to select blocks for export
|
||||
this.selectorWorkspace =
|
||||
Blockly.inject('exportSelector',
|
||||
{collapse: false,
|
||||
toolbox: this.toolbox,
|
||||
grid:
|
||||
{spacing: 20,
|
||||
length: 3,
|
||||
colour: '#ccc',
|
||||
snap: true}
|
||||
});
|
||||
BlockExporterView = function(blockOptions) {
|
||||
// Map of block types to BlockOption objects to select from.
|
||||
this.blockOptions = blockOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the toolbox of this instance of BlockExporterView.
|
||||
* Set the block options in the selector of this instance of
|
||||
* BlockExporterView.
|
||||
*
|
||||
* @param {Element} toolboxXml - Xml for the toolbox of the selector workspace.
|
||||
* @param {!Object} blockOptions - Map of block types to BlockOption objects.
|
||||
*/
|
||||
BlockExporterView.prototype.setToolbox = function(toolboxXml) {
|
||||
// Parse the provided toolbox tree into a consistent DOM format.
|
||||
this.toolbox = Blockly.Options.parseToolboxTree(toolboxXml);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the toolbox in the workspace. Used to update the toolbox upon
|
||||
* switching between Block Factory tab and Block Exporter Tab.
|
||||
*/
|
||||
BlockExporterView.prototype.renderToolbox = function() {
|
||||
this.selectorWorkspace.updateToolbox(this.toolbox);
|
||||
BlockExporterView.prototype.setBlockOptions = function(blockOptions) {
|
||||
this.blockOptions = blockOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,56 +72,74 @@ BlockExporterView.prototype.updateHelperText = function(newText, opt_append) {
|
||||
|
||||
/**
|
||||
* Updates the helper text to show list of currently selected blocks.
|
||||
*
|
||||
* @param {!Array.<string>} selectedBlockTypes - Array of blocks selected in workspace.
|
||||
*/
|
||||
BlockExporterView.prototype.listSelectedBlocks = function(selectedBlockTypes) {
|
||||
var selectedBlocksText = selectedBlockTypes.join(",\n ");
|
||||
BlockExporterView.prototype.listSelectedBlocks = function() {
|
||||
|
||||
var selectedBlocksText = this.getSelectedBlockTypes().join(",\n ");
|
||||
goog.dom.getElement('selectedBlocksText').textContent = selectedBlocksText;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders block of given type on selector workspace assuming block has already
|
||||
* been defined.
|
||||
* Selects a given block type in the selector.
|
||||
*
|
||||
* @param {string} blockType - Type of block to add to selector workspce.
|
||||
* @param {string} blockType - Type of block to selector.
|
||||
*/
|
||||
BlockExporterView.prototype.addBlock = function(blockType) {
|
||||
var newBlock = this.selectorWorkspace.newBlock(blockType);
|
||||
newBlock.initSvg();
|
||||
newBlock.render();
|
||||
BlockExporterView.prototype.select = function(blockType) {
|
||||
this.blockOptions[blockType].setSelected(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a block from the selector workspace.
|
||||
* Deselects a block in the selector.
|
||||
*
|
||||
* @param {!Blockly.Block} block - Type of block to add to selector workspce.
|
||||
*/
|
||||
BlockExporterView.prototype.removeBlock = function(block) {
|
||||
block.dispose();
|
||||
BlockExporterView.prototype.deselect = function(blockType) {
|
||||
this.blockOptions[blockType].setSelected(false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears selector workspace.
|
||||
* Deselects all blocks.
|
||||
*/
|
||||
BlockExporterView.prototype.clearSelectorWorkspace = function() {
|
||||
this.selectorWorkspace.clear();
|
||||
BlockExporterView.prototype.deselectAllBlocks = function() {
|
||||
for (var blockType in this.blockOptions) {
|
||||
this.deselect(blockType);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Neatly layout the blocks in selector workspace.
|
||||
* Given an array of selected blocks, selects these blocks in the view, marking
|
||||
* the checkboxes accordingly.
|
||||
*
|
||||
* @param {Array.<Blockly.Block>} blockTypes - Array of block types to select.
|
||||
*/
|
||||
BlockExporterView.prototype.cleanUpSelectorWorkspace = function() {
|
||||
this.selectorWorkspace.cleanUp();
|
||||
BlockExporterView.prototype.setSelectedBlockTypes = function(blockTypes) {
|
||||
for (var i = 0, blockType; blockType = blockTypes[i]; i++) {
|
||||
this.select(blockType);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns array of selected blocks.
|
||||
*
|
||||
* @return {Array.<Blockly.Block>} Array of all blocks in selector workspace.
|
||||
* @return {!Array.<!string>} Array of all selected block types.
|
||||
*/
|
||||
BlockExporterView.prototype.getSelectedBlocks = function() {
|
||||
return this.selectorWorkspace.getAllBlocks();
|
||||
BlockExporterView.prototype.getSelectedBlockTypes = function() {
|
||||
var selectedTypes = [];
|
||||
for (var blockType in this.blockOptions) {
|
||||
var blockOption = this.blockOptions[blockType];
|
||||
if (blockOption.isSelected()) {
|
||||
selectedTypes.push(blockType);
|
||||
}
|
||||
}
|
||||
return selectedTypes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Centers the preview block of each block option in the exporter selector.
|
||||
*/
|
||||
BlockExporterView.prototype.centerPreviewBlocks = function() {
|
||||
for (var blockType in this.blockOptions) {
|
||||
this.blockOptions[blockType].centerBlock();
|
||||
}
|
||||
};
|
||||
|
||||
181
demos/blocklyfactory/block_option.js
Normal file
181
demos/blocklyfactory/block_option.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* You may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Javascript for the BlockOption class, used to represent each of
|
||||
* the various blocks that you may select. Each block option has a checkbox,
|
||||
* a label, and a preview workspace through which to view the block.
|
||||
*
|
||||
* @author quachtina96 (Tina Quach)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('BlockOption');
|
||||
goog.require('goog.dom');
|
||||
|
||||
/**
|
||||
* BlockOption Class
|
||||
* A block option includes checkbox, label, and div element that shows a preview
|
||||
* of the block.
|
||||
* @constructor
|
||||
*
|
||||
* @param {!Element} blockSelector - Scrollable div that will contain the
|
||||
* block options for the selector.
|
||||
* @param {!string} blockType - Type of block for which to create an option.
|
||||
* @param {!Element} previewBlockXml - Xml element containing the preview block.
|
||||
*/
|
||||
var BlockOption = function(blockSelector, blockType, previewBlockXml) {
|
||||
// The div to contain the block option.
|
||||
this.blockSelector = blockSelector;
|
||||
// The type of block represented by the option.
|
||||
this.blockType = blockType;
|
||||
// The checkbox for the option. Set in createDom.
|
||||
this.checkbox = null;
|
||||
// The dom for the option. Set in createDom.
|
||||
this.dom = null;
|
||||
// Xml element containing the preview block.
|
||||
this.previewBlockXml = previewBlockXml;
|
||||
// Workspace containing preview of block. Set upon injection of workspace in
|
||||
// showPreviewBlock.
|
||||
this.previewWorkspace = null;
|
||||
// Whether or not block the option is selected.
|
||||
this.selected = false;
|
||||
// Using this.selected rather than this.checkbox.checked allows for proper
|
||||
// handling of click events on the block option; Without this, clicking
|
||||
// directly on the checkbox does not toggle selection.
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the dom for a single block option. Includes checkbox, label, and div
|
||||
* in which to inject the preview block.
|
||||
*
|
||||
* @return {!Element} Root node of the selector dom which consists of a
|
||||
* checkbox, a label, and a fixed size preview workspace per block.
|
||||
*/
|
||||
BlockOption.prototype.createDom = function() {
|
||||
// Create the div for the block option.
|
||||
var blockOptContainer = goog.dom.createDom('div', {
|
||||
'id': this.blockType,
|
||||
'class': 'blockOption'
|
||||
}, ''); // Empty quotes for empty div.
|
||||
|
||||
// Create and append div in which to inject the workspace for viewing the
|
||||
// block option.
|
||||
var blockOptionPreview = goog.dom.createDom('div', {
|
||||
'id' : this.blockType + '_workspace',
|
||||
'class': 'blockOption_preview'
|
||||
}, '');
|
||||
goog.dom.appendChild(blockOptContainer,blockOptionPreview);
|
||||
|
||||
// Create and append container to hold checkbox and label.
|
||||
var checkLabelContainer = goog.dom.createDom('div', {
|
||||
'class': 'blockOption_checkLabel'
|
||||
}, '');
|
||||
goog.dom.appendChild(blockOptContainer,checkLabelContainer);
|
||||
|
||||
// Create and append container for checkbox.
|
||||
var checkContainer = goog.dom.createDom('div', {
|
||||
'class': 'blockOption_check'
|
||||
}, '');
|
||||
goog.dom.appendChild(checkLabelContainer, checkContainer);
|
||||
|
||||
// Create and append checkbox.
|
||||
this.checkbox = goog.dom.createDom('input', {
|
||||
'type': 'checkbox',
|
||||
'id': this.blockType + '_check'
|
||||
}, '');
|
||||
goog.dom.appendChild(checkContainer, this.checkbox);
|
||||
|
||||
// Create and append container for block label.
|
||||
var labelContainer = goog.dom.createDom('div', {
|
||||
'class': 'blockOption_label'
|
||||
}, '');
|
||||
goog.dom.appendChild(checkLabelContainer, labelContainer);
|
||||
|
||||
// Create and append text node for the label.
|
||||
var labelText = goog.dom.createDom('p', {
|
||||
'id': this.blockType + '_text'
|
||||
}, this.blockType);
|
||||
goog.dom.appendChild(labelContainer, labelText);
|
||||
|
||||
this.dom = blockOptContainer;
|
||||
return this.dom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Injects a workspace containing the block into the block option's preview div.
|
||||
*/
|
||||
BlockOption.prototype.showPreviewBlock = function() {
|
||||
// Get ID of preview workspace.
|
||||
var blockOptPreviewID = this.dom.id + '_workspace';
|
||||
|
||||
// Inject preview block.
|
||||
var workspace = Blockly.inject(blockOptPreviewID, {readOnly:true});
|
||||
Blockly.Xml.domToWorkspace(this.previewBlockXml, workspace);
|
||||
this.previewWorkspace = workspace;
|
||||
|
||||
// Center the preview block in the workspace.
|
||||
this.centerBlock();
|
||||
};
|
||||
|
||||
/**
|
||||
* Centers the preview block in the workspace.
|
||||
*/
|
||||
BlockOption.prototype.centerBlock = function() {
|
||||
// Get metrics.
|
||||
var block = this.previewWorkspace.getTopBlocks()[0];
|
||||
var blockMetrics = block.getHeightWidth();
|
||||
var blockCoordinates = block.getRelativeToSurfaceXY();
|
||||
var workspaceMetrics = this.previewWorkspace.getMetrics();
|
||||
|
||||
// Calculate new coordinates.
|
||||
var x = workspaceMetrics.viewWidth/2 - blockMetrics['width']/2 -
|
||||
blockCoordinates.x;
|
||||
var y = workspaceMetrics.viewHeight/2 - blockMetrics['height']/2 -
|
||||
blockCoordinates.y;
|
||||
|
||||
// Move block.
|
||||
block.moveBy(x, y);
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects or deselects the block option.
|
||||
*
|
||||
* @param {!boolean} selected - True if selecting option, false if deselecting
|
||||
* option.
|
||||
*/
|
||||
BlockOption.prototype.setSelected = function(selected) {
|
||||
this.selected = selected;
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = selected;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns boolean telling whether or not block is selected.
|
||||
*
|
||||
* @return {!boolean} True if selecting option, false if deselecting
|
||||
* option.
|
||||
*/
|
||||
BlockOption.prototype.isSelected = function() {
|
||||
return this.selected;
|
||||
};
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ button, .buttonStyle {
|
||||
#exportSelector {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 60%;
|
||||
height: 70%;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
@@ -255,6 +255,45 @@ button, .buttonStyle {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#blockSelector {
|
||||
background-color: #eee;
|
||||
border: 1px solid lightgrey;
|
||||
width: 80%;
|
||||
height: 90%;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Exporter Block Option */
|
||||
|
||||
.blockOption {
|
||||
background-color: #eee;
|
||||
padding: 15px 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.blockOption_check_label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.blockOption_check {
|
||||
float: left;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.blockOption_label {
|
||||
float: left;
|
||||
max-width: inherit;
|
||||
overflow-y: scroll;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.blockOption_preview {
|
||||
height: 100px;
|
||||
padding-top: 10px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
|
||||
.tab {
|
||||
@@ -434,19 +473,19 @@ td {
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Dropdown Content (Hidden by Default) */
|
||||
.dropdown-content {
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,.2);
|
||||
display: none;
|
||||
min-width: 170px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,.2);
|
||||
display: none;
|
||||
min-width: 170px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<script src="/storage.js"></script>
|
||||
<script src="../../../closure-library/closure/goog/base.js"></script>
|
||||
<script src="factory_utils.js"></script>
|
||||
<script src="block_option.js"></script>
|
||||
<script src="factory.js"></script>
|
||||
<script src="block_library_view.js"></script>
|
||||
<script src="block_library_storage.js"></script>
|
||||
@@ -38,7 +39,7 @@
|
||||
blocklyFactory.init();
|
||||
};
|
||||
window.addEventListener('load', init);
|
||||
</script>
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
@@ -50,7 +51,7 @@
|
||||
<div id="tabContainer">
|
||||
<div id="blockFactory_tab" class="tab tabon"> Block Factory</div>
|
||||
<div id="workspaceFactory_tab" class="tab taboff"> Workspace Factory</div>
|
||||
<div id="blocklibraryExporter_tab" class="tab taboff"> Exporter</div>
|
||||
<div id="blocklibraryExporter_tab" class="tab taboff"> Block Exporter</div>
|
||||
</div>
|
||||
|
||||
<!-- Exporter tab -->
|
||||
@@ -64,15 +65,12 @@
|
||||
<div class='dropdown'>
|
||||
<button id="button_setBlocks">Select From Library</button>
|
||||
<div id="dropdownDiv_setBlocks" class="dropdown-content">
|
||||
<a id='dropdown_addAllFromLib'>All Stored</a>
|
||||
<a id='dropdown_addAllUsed'>All Used</a>
|
||||
<a id='dropdown_clearSelected'>Remove All</a>
|
||||
<a id='dropdown_addAllFromLib' title="Select all block library blocks.">All Stored</a>
|
||||
<a id='dropdown_addAllUsed' title="Select all block library blocks used in workspace factory.">All Used</a>
|
||||
<a id='dropdown_clearSelected' title="Clear selected blocks.">Clear</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inject exportSelectorWorkspace into this div -->
|
||||
<div id="selectorWorkspace"></div>
|
||||
|
||||
<div id="blockSelector"></div>
|
||||
</div>
|
||||
|
||||
<!-- Users may customize export settings through this form -->
|
||||
@@ -80,6 +78,7 @@
|
||||
<br>
|
||||
<h3> Export Settings </h3>
|
||||
<form id="exportSettingsForm">
|
||||
|
||||
<div id="selectedBlocksTextContainer">
|
||||
<p>Currently Selected:</p>
|
||||
<p id="selectedBlocksText"></p>
|
||||
@@ -114,7 +113,7 @@
|
||||
</div>
|
||||
<br>
|
||||
</form>
|
||||
<button id="exporterSubmitButton"> Export </button>
|
||||
<button id="exporterSubmitButton" title="Download block starter code as specified in export settings."> Export </button>
|
||||
</div>
|
||||
<div id="exportPreview">
|
||||
<br>
|
||||
@@ -305,9 +304,6 @@
|
||||
</h3>
|
||||
</td>
|
||||
<td id="buttonContainer">
|
||||
<button id="linkButton" title="Save and link to blocks.">
|
||||
<img src="link.png" height="21" width="21">
|
||||
</button>
|
||||
<button id="linkButton" title="Save and link to blocks.">
|
||||
<img src="link.png" height="21" width="21">
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user