From ab850b9863a3b796a35a590eb7f9e432dda31378 Mon Sep 17 00:00:00 2001 From: Emma Dauterman Date: Tue, 23 Aug 2016 10:48:36 -0700 Subject: [PATCH] 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 --- demos/blocklyfactory/app_controller.js | 42 ++++++++---- demos/blocklyfactory/factory.css | 36 ++++++++--- demos/blocklyfactory/index.html | 21 +++--- .../workspacefactory/wfactory_controller.js | 64 ++++++++++--------- .../workspacefactory/wfactory_init.js | 23 ++++--- .../workspacefactory/wfactory_view.js | 23 +++++-- 6 files changed, 130 insertions(+), 79 deletions(-) diff --git a/demos/blocklyfactory/app_controller.js b/demos/blocklyfactory/app_controller.js index 0e686631b..c9c8e7c71 100644 --- a/demos/blocklyfactory/app_controller.js +++ b/demos/blocklyfactory/app_controller.js @@ -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); diff --git a/demos/blocklyfactory/factory.css b/demos/blocklyfactory/factory.css index 4ce84e68c..68115f6ad 100644 --- a/demos/blocklyfactory/factory.css +++ b/demos/blocklyfactory/factory.css @@ -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 { diff --git a/demos/blocklyfactory/index.html b/demos/blocklyfactory/index.html index febef6609..7f7c3129e 100644 --- a/demos/blocklyfactory/index.html +++ b/demos/blocklyfactory/index.html @@ -164,13 +164,13 @@ - - + +

-

Workspace Editor:

-

Drag blocks into your toolbox:

+

Edit

+

Drag blocks into the workspace to configure the toolbox in your custom workspace.

@@ -180,8 +180,8 @@
Toolbox Workspace
-
Your categories will appear here

 

@@ -199,7 +199,7 @@ -

 

+
diff --git a/demos/blocklyfactory/workspacefactory/wfactory_controller.js b/demos/blocklyfactory/workspacefactory/wfactory_controller.js index da5749921..0db36c7d8 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_controller.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_controller.js @@ -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('')); 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); } diff --git a/demos/blocklyfactory/workspacefactory/wfactory_init.js b/demos/blocklyfactory/workspacefactory/wfactory_init.js index 09cb479b6..d306222a9 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_init.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_init.js @@ -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) { diff --git a/demos/blocklyfactory/workspacefactory/wfactory_view.js b/demos/blocklyfactory/workspacefactory/wfactory_view.js index ad855709c..5f713960c 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_view.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_view.js @@ -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; };