Blockly Factory: Workspace Factory UI (#577)

* Some UI changes made

* CSS changes

* Done with UI tweaks

* Changing arrow key event listeners, also changing category header in category list

* Fixed bug with error on updating toolbox with read only

* Changes to factory.css and workspacefactory view

* Bug fixes and UI changes and refactoring to have enableKeyEvents in wfactory controller

* Uncommented catch

* Changes to app_controller.js to use constants, other nit changes

* Nit line breaks
This commit is contained in:
Emma Dauterman
2016-08-23 10:48:36 -07:00
committed by picklesrus
parent 6953afb0b5
commit ab850b9863
6 changed files with 130 additions and 79 deletions

View File

@@ -56,16 +56,23 @@ AppController = function() {
new BlockExporterController(this.blockLibraryController.storage);
// Map of tab type to the div element for the tab.
this.tabMap = {
'BLOCK_FACTORY' : goog.dom.getElement('blockFactory_tab'),
'WORKSPACE_FACTORY': goog.dom.getElement('workspaceFactory_tab'),
'EXPORTER' : goog.dom.getElement('blocklibraryExporter_tab')
};
this.tabMap = Object.create(null);
this.tabMap[AppController.BLOCK_FACTORY] =
goog.dom.getElement('blockFactory_tab');
this.tabMap[AppController.WORKSPACE_FACTORY] =
goog.dom.getElement('workspaceFactory_tab');
this.tabMap[AppController.EXPORTER] =
goog.dom.getElement('blocklibraryExporter_tab');
// Selected tab.
this.selectedTab = 'BLOCK_FACTORY';
this.selectedTab = AppController.BLOCK_FACTORY;
};
// Constant values representing the three tabs in the controller.
AppController.BLOCK_FACTORY = 'BLOCK_FACTORY';
AppController.WORKSPACE_FACTORY = 'WORKSPACE_FACTORY';
AppController.EXPORTER = 'EXPORTER';
/**
* Tied to the 'Import Block Library' button. Imports block library from file to
* Block Factory. Expects user to upload a single file of JSON mapping each
@@ -256,7 +263,8 @@ AppController.prototype.addTabHandlers = function(tabMap) {
* Set the selected tab.
* @private
*
* @param {string} tabName 'BLOCK_FACTORY', 'WORKSPACE_FACTORY', or 'EXPORTER'
* @param {string} tabName AppController.BLOCK_FACTORY,
* AppController.WORKSPACE_FACTORY, or AppController.EXPORTER
*/
AppController.prototype.setSelected_ = function(tabName) {
this.selectedTab = tabName;
@@ -266,7 +274,8 @@ AppController.prototype.setSelected_ = function(tabName) {
* Creates the tab click handler specific to the tab specified.
* @private
*
* @param {string} tabName 'BLOCK_FACTORY', 'WORKSPACE_FACTORY', or 'EXPORTER'
* @param {string} tabName AppController.BLOCK_FACTORY,
* AppController.WORKSPACE_FACTORY, or AppController.EXPORTER
* @return {Function} The tab click handler.
*/
AppController.prototype.makeTabClickHandler_ = function(tabName) {
@@ -283,14 +292,19 @@ AppController.prototype.makeTabClickHandler_ = function(tabName) {
*/
AppController.prototype.onTab = function() {
// Get tab div elements.
var blockFactoryTab = this.tabMap['BLOCK_FACTORY'];
var exporterTab = this.tabMap['EXPORTER'];
var workspaceFactoryTab = this.tabMap['WORKSPACE_FACTORY'];
var blockFactoryTab = this.tabMap[AppController.BLOCK_FACTORY];
var exporterTab = this.tabMap[AppController.EXPORTER];
var workspaceFactoryTab = this.tabMap[AppController.WORKSPACE_FACTORY];
// Turn selected tab on and other tabs off.
this.styleTabs_();
if (this.selectedTab == 'EXPORTER') {
// Only enable key events in workspace factory if workspace factory tab is
// selected.
this.workspaceFactoryController.keyEventsEnabled =
this.selectedTab == AppController.WORKSPACE_FACTORY;
if (this.selectedTab == AppController.EXPORTER) {
// Update toolbox to reflect current block library.
this.exporter.updateToolbox();
@@ -309,12 +323,12 @@ AppController.prototype.onTab = function() {
FactoryUtils.show('blockLibraryExporter');
FactoryUtils.hide('workspaceFactoryContent');
} else if (this.selectedTab == 'BLOCK_FACTORY') {
} else if (this.selectedTab == AppController.BLOCK_FACTORY) {
// Hide container of exporter.
FactoryUtils.hide('blockLibraryExporter');
FactoryUtils.hide('workspaceFactoryContent');
} else if (this.selectedTab == 'WORKSPACE_FACTORY') {
} else if (this.selectedTab == AppController.WORKSPACE_FACTORY) {
// Update block library category.
var categoryXml = this.exporter.getBlockLibCategory();
this.workspaceFactoryController.setBlockLibCategory(categoryXml);

View File

@@ -299,8 +299,8 @@ aside {
}
td.tabon {
border-bottom-color: #ddd !important;
background-color: #ddd;
background-color: #ccc;
border-bottom-color: #ccc;
padding: 5px 19px;
}
@@ -331,9 +331,15 @@ td {
z-index: -1;
}
#workspaceTabs {
background-color: #f8f8f8;
border: 1px solid #ccc;
display: table;
}
#toolbox_section {
height: 480px;
width: 80%;
height: 405px;
width: 60%;
position: relative;
}
@@ -343,20 +349,34 @@ td {
}
#preview_blocks {
height: 300px;
height: 395px;
width: 100%;
}
#createDiv {
width: 70%;
padding: 0.5%;
width: 60%;
}
#previewDiv {
width: 30%;
border: 10px solid #eee;
margin-right: 0.5%;
width: 35%;
}
#previewBorder {
border: 5px solid #ddd;
padding: 2%;
}
#toolbox_div, #preload_div {
width: 20%;
margin-right: 5%;
width: 35%;
}
#workspace_options {
max-height: 250px;
overflow-y: scroll;
}
#disable_div {

View File

@@ -164,13 +164,13 @@
</div>
</div>
<button id="button_print">Print Toolbox</button>
<button id="button_clear">Clear Toolbox</button>
<button id="button_clear">Clear</button>
</p>
<section id="createDiv">
<h3>Workspace Editor:</h3>
<p id="editHelpText">Drag blocks into your toolbox:</p>
<h3>Edit</h3>
<p id="editHelpText">Drag blocks into the workspace to configure the toolbox in your custom workspace.</p>
<table id='workspaceTabs' style="width:auto">
<td id="tab_toolbox" class="tabon">Toolbox</td>
<td id="tab_preload" class="taboff">Workspace</td>
@@ -180,8 +180,8 @@
<div id='disable_div'></div>
</section>
<aside id="toolbox_div">
<p id="categoryHeader">Your categories will appear here</p>
<table id="categoryTable" style="width:auto">
<td id="tab_help">Your categories will appear here</td>
</table>
<p>&nbsp;</p>
@@ -199,7 +199,7 @@
<button id="button_up" class="large">&#8593;</button>
<button id="button_down" class="large">&#8595;</button>
<p>&nbsp;</p>
<br>
<div class='dropdown'>
<button id="button_editCategory">Edit Category</button>
<div id="dropdownDiv_editCategory" class="dropdown-content">
@@ -222,7 +222,7 @@
<aside id='preload_div' style='display:none'>
<p>Configure the <a href="https://developers.google.com/blockly/guides/get-started/web">options</a> for your Blockly inject call.</p>
<button class="small" id="button_standardOptions">Standard Options</button>
<button class="small" id="button_standardOptions">Reset Options</button>
<form id="workspace_options">
<input type="checkbox" id="option_collapse_checkbox" class="optionsInput">Collapsible Blocks<br>
<input type="checkbox" id="option_comments_checkbox" class="optionsInput">Comments for Blocks<br>
@@ -258,8 +258,11 @@
</section>
<aside id="previewDiv">
<h3>Preview Workspace:</h3>
<div id="preview_blocks" class="content"></div>
<div id="previewBorder">
<h3>Preview</h3>
<p>This is what your custom workspace will look like.</p>
<div id="preview_blocks" class="content"></div>
</div>
</aside>
</div>

View File

@@ -82,6 +82,8 @@ WorkspaceFactoryController = function(toolboxName, toolboxDiv, previewDiv) {
this.generator = new WorkspaceFactoryGenerator(this.model);
// Tracks which editing mode the user is in. Toolbox mode on start.
this.selectedMode = WorkspaceFactoryController.MODE_TOOLBOX;
// True if key events are enabled, false otherwise.
this.keyEventsEnabled = true;
};
// Toolbox editing mode. Changes the user makes to the workspace updates the
@@ -399,24 +401,30 @@ WorkspaceFactoryController.prototype.updatePreview = function() {
if (this.selectedMode == WorkspaceFactoryController.MODE_TOOLBOX) {
// If currently editing the toolbox.
// Get toolbox XML.
var tree = Blockly.Options.parseToolboxTree
(this.generator.generateToolboxXml());
// No categories, creates a simple flyout.
if (tree.getElementsByTagName('category').length == 0) {
// Only update the toolbox if not in read only mode.
if (!this.model.options['readOnly']) {
// Get toolbox XML.
var tree = Blockly.Options.parseToolboxTree
(this.generator.generateToolboxXml());
// No categories, creates a simple flyout.
if (this.previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Switch to simple flyout, more expensive.
if (tree.getElementsByTagName('category').length == 0) {
// No categories, creates a simple flyout.
if (this.previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Switch to simple flyout, expensive.
} else {
this.previewWorkspace.updateToolbox(tree);
}
} else {
this.previewWorkspace.flyout_.show(tree.childNodes);
}
} else {
// Uses categories, creates a toolbox.
if (!this.previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Create a toolbox, more expensive.
} else {
this.previewWorkspace.toolbox_.populate_(tree);
// Uses categories, creates a toolbox.
if (!this.previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Create a toolbox, expensive.
} else {
this.previewWorkspace.updateToolbox(tree);
}
}
}
// Update pre-loaded blocks in the preview workspace to make sure that
@@ -425,6 +433,7 @@ WorkspaceFactoryController.prototype.updatePreview = function() {
this.previewWorkspace.clear();
Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
this.previewWorkspace);
} else if (this.selectedMode == WorkspaceFactoryController.MODE_PRELOAD){
// If currently editing the pre-loaded workspace.
this.previewWorkspace.clear();
@@ -676,6 +685,7 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
return;
}
Blockly.Events.disable();
var controller = this;
var reader = new FileReader();
@@ -697,9 +707,11 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
// Throw error if invalid mode.
throw new Error("Unknown import mode: " + importMode);
}
} catch(e) {
alert('Cannot load XML from file.');
console.log(e);
} catch(e) {
alert('Cannot load XML from file.');
console.log(e);
} finally {
Blockly.Events.enable();
}
}
@@ -773,8 +785,6 @@ WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) {
this.view.updateState(this.model.getIndexByElementId
(this.model.getSelectedId()), this.model.getSelected());
this.saveStateFromWorkspace();
this.saveStateFromWorkspace();
// Allow the user to set default configuration options for a single flyout
// or multiple categories.
@@ -828,15 +838,15 @@ WorkspaceFactoryController.prototype.importPreloadFromTree_ = function(tree) {
}
/**
* Clears the toolbox editing area completely, deleting all categories and all
* blocks in the model and view. Sets the mode to toolbox mode. Tied to "Clear
* Toolbox" button.
* Clears the editing area completely, deleting all categories and all
* blocks in the model and view and all pre-loaded blocks. Tied to the
* "Clear" button.
*/
WorkspaceFactoryController.prototype.clearToolbox = function() {
this.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
WorkspaceFactoryController.prototype.clearAll = function() {
var hasCategories = this.model.hasElements();
this.model.clearToolboxList();
this.view.clearToolboxTabs();
this.model.savePreloadXml(Blockly.Xml.textToDom('<xml></xml>'));
this.view.addEmptyCategoryMessage();
this.view.updateState(-1, null);
this.toolboxWorkspace.clear();
@@ -998,8 +1008,6 @@ WorkspaceFactoryController.prototype.setMode = function(mode) {
if (mode == WorkspaceFactoryController.MODE_TOOLBOX) {
// Open the toolbox editing space.
document.getElementById('editHelpText').textContent =
'Drag blocks into your toolbox:';
this.model.savePreloadXml
(Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
this.clearAndLoadXml_(this.model.getSelectedXml());
@@ -1007,8 +1015,6 @@ WorkspaceFactoryController.prototype.setMode = function(mode) {
(this.model.getSelected()));
} else {
// Open the pre-loaded workspace editing space.
document.getElementById('editHelpText').textContent =
'Drag blocks into your pre-loaded workspace:';
if (this.model.getSelected()) {
this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace);
}

View File

@@ -235,12 +235,6 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
document.getElementById('dropdownDiv_load').classList.remove("show");
document.getElementById('dropdownDiv_importBlocks').classList.
remove("show");
})
document.getElementById('button_print').addEventListener
('click',
function() {
controller.printConfig();
});
document.getElementById('button_up').addEventListener
@@ -351,11 +345,11 @@ document.getElementById('button_importBlocks').addEventListener
document.getElementById('button_clear').addEventListener
('click',
function() {
controller.clearToolbox();
document.getElementById('dropdownDiv_importBlocks').classList.
remove("show");
document.getElementById('dropdownDiv_export').classList.remove("show");
document.getElementById('dropdownDiv_load').classList.remove("show");
controller.clearAll();
});
document.getElementById('dropdown_addShadow').addEventListener
@@ -394,13 +388,18 @@ document.getElementById('button_importBlocks').addEventListener
*/
WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
// Use up and down arrow keys to move categories.
// TODO(evd2014): When merge with next CL for editing preloaded blocks, make
// sure mode is toolbox.
window.addEventListener('keydown', function(e) {
if (this.selectedTab != 'WORKSPACE_FACTORY' && e.keyCode == 38) {
// Don't let arrow keys have any effect if not in Workspace Factory
// editing the toolbox.
if (!(controller.keyEventsEnabled && controller.selectedMode
== WorkspaceFactoryController.MODE_TOOLBOX)) {
return;
}
if (e.keyCode == 38) {
// Arrow up.
controller.moveElement(-1);
} else if (this.selectedTab != 'WORKSPACE_FACTORY' && e.keyCode == 40) {
} else if (e.keyCode == 40) {
// Arrow down.
controller.moveElement(1);
}
@@ -414,7 +413,7 @@ WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
!block.getSurroundParent()) ||
(!controller.isUserGenShadowBlock(block.id) && block.getSurroundParent()
&& controller.isUserGenShadowBlock(block.getSurroundParent().id)));
}
};
// Add change listeners for toolbox workspace in workspace factory.
controller.toolboxWorkspace.addChangeListener(function(e) {

View File

@@ -52,7 +52,7 @@ WorkspaceFactoryView.prototype.addCategoryRow =
var table = document.getElementById('categoryTable');
// Delete help label and enable category buttons if it's the first category.
if (firstCategory) {
table.deleteRow(0);
document.getElementById('categoryHeader').textContent = 'Your Categories:';
}
// Create tab.
var count = table.rows.length;
@@ -94,8 +94,8 @@ WorkspaceFactoryView.prototype.deleteElementRow = function(id, index) {
WorkspaceFactoryView.prototype.addEmptyCategoryMessage = function() {
var table = document.getElementById('categoryTable');
if (table.rows.length == 0) {
var row = table.insertRow(0);
row.textContent = 'Your categories will appear here';
document.getElementById('categoryHeader').textContent =
'Your categories will appear here';
}
}
@@ -216,11 +216,14 @@ WorkspaceFactoryView.prototype.moveTabToIndex =
oldIndex >= table.rows.length) {
throw new Error('Index out of bounds when moving tab in the view.');
}
if (newIndex < oldIndex) { // Inserting before.
if (newIndex < oldIndex) {
// Inserting before.
var row = table.insertRow(newIndex);
row.appendChild(this.tabMap[id]);
table.deleteRow(oldIndex + 1);
} else { // Inserting after.
} else {
// Inserting after.
var row = table.insertRow(newIndex + 1);
row.appendChild(this.tabMap[id]);
table.deleteRow(oldIndex);
@@ -296,6 +299,7 @@ WorkspaceFactoryView.prototype.clearToolboxTabs = function() {
var oldCategoryTable = document.getElementById('categoryTable');
var newCategoryTable = document.createElement('table');
newCategoryTable.id = 'categoryTable';
newCategoryTable.style.width = 'auto';
oldCategoryTable.parentElement.replaceChild(newCategoryTable,
oldCategoryTable);
};
@@ -370,8 +374,13 @@ WorkspaceFactoryView.prototype.setModeSelection = function(mode) {
* WorkspaceFactoryController.MODE_PRELOAD).
*/
WorkspaceFactoryView.prototype.updateHelpText = function(mode) {
var helpText = 'Drag your blocks into your ' + (mode ==
WorkspaceFactoryController.MODE_TOOLBOX ? 'toolbox: ' : 'pre-loaded workspace: ');
if (mode == WorkspaceFactoryController.MODE_TOOLBOX) {
var helpText = 'Drag blocks into the workspace to configure the toolbox '
+ 'in your custom workspace.';
} else {
var helpText = 'Drag blocks into the workspace to pre-load them in your '
+ 'custom workspace.'
}
document.getElementById('editHelpText').textContent = helpText;
};