A Blockly plugin that allows you to drag, select and manipulate multiple blocks in the workspace.
yarn add @mit-app-inventor/blockly-plugin-workspace-multiselect
npm install @mit-app-inventor/blockly-plugin-workspace-multiselect --save
import * as Blockly from 'blockly';
import {Multiselect} from '@mit-app-inventor/blockly-plugin-workspace-multiselect';
options = {
toolbox: toolboxCategories,
// The plugin should work with other plugins
// that require a custom block dragger, like
// the scroll-options plugin as this plugin
// was updated to not require a custom dragger.
// Double click the blocks to collapse/expand
// them (A feature from MIT App Inventor).
useDoubleClick: false,
// Bump neighbours after dragging to avoid overlapping.
bumpNeighbours: false,
// Keep the fields of multiple selected same-type blocks with the same value
// See note below.
multiFieldUpdate: true,
// Auto focus the workspace when the mouse enters.
workspaceAutoFocus: true,
// Use custom icon for the multi select controls.
multiselectIcon: {
hideIcon: false,
weight: 3,
enabledIcon: 'https://github.com/mit-cml/workspace-multiselect/raw/main/test/media/select.svg',
disabledIcon: 'https://github.com/mit-cml/workspace-multiselect/raw/main/test/media/unselect.svg',
},
// Keys for multi-selection mode switch. Any key value is possible (see MDN docs).
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
// The best support (default) is given for Shift. Provide an empty array []
// will revert to the default key.
// One thing to make sure is that the multiSelectKeys do not overlap with the
// keys defined for the shortcut actions as it will lead to a cascade of calls,
// which is not ideal for the browser.
multiSelectKeys: ['Shift'],
multiselectCopyPaste: {
// Enable the copy/paste accross tabs feature (true by default).
crossTab: true,
// Show the copy/paste menu entries (true by default).
menu: true,
},
};
// Inject Blockly.
const workspace = Blockly.inject('blocklyDiv', options);
// Initialize plugin.
const multiselectPlugin = new Multiselect(workspace);
multiselectPlugin.init(options);
bumpNeighbours: true
.Duplicate
menu will duplicate the selected most top block in the block stack and all the children blocks of that most top block. The selection will be changed to all newly created duplicate blocks' most top blocks.Add Comment
/ Remove Comment
will instead read as Add Comment (X)
/ Remove Comment (X)
, and will add / remove comment buttons to all the selected blocks.Inline Inputs
/ External Inputs
will instead read as Inline Inputs (X)
/ External Inputs (X)
, and will convert the input format with all the selected blocks.Collapse Block
/ Expand Block
will instead read as Collapse Block (X)
/ Expand Block (X)
, and will only apply to the selected most top block in the block stack.Disable Block
/ Enable Block
will instead read as Disable Block (X)
/ Enable Block (X)
, and will only apply to the selected most top block in the block stack. All the children blocks of that most top block will also get disabled.X
means the currently applicable number of selected blocks by the user, and (X)
will only be shown when X is greater than 1.Delete [X] Blocks
represents the count of the selected most top block in the block stack as well as all children of those selected most top block, and delete the blocks mentioned.Select all Blocks
in that workspace.Ctrl/Alt + A
, you can select all the blocks in the current workspace. Ctrl/Alt + C
to copy the selected blocks, Ctrl/Alt + X
to cut the selected blocks to the clipboard, and Ctrl/Alt + V
to paste all the blocks currently in the clipboard and get all the newly pasted blocks selected, these will only apply to the selected most top block in the block stack.useDoubleClick: true
.Copy to backpack (Y)
will become (X) Copy to backpack (Y)
, where Y
represents the number of blocks that are already in the backpack, and X
represents the number of top most blocks that can be copied to the backpack. The Copy to backpack (Y)
menu will only be disabled when none of the selected blocks can be copied to the backpack, and it will only be applied to the selected most top block in the block stack.When the multiFieldUpdate option is enable, the plugin will automatically update the fields of all selected blocks with the
same type. This can cause issues when you have multiple field on a block and one field is dependent on another.
For example, if you have a block with a dropdown field and another, dependent, field which is programmatically updated
(e.g. in the dropdown field's validator) based on the value of the dropdown field, then the
multi-field update may interfere with the programmatic update. In this situation, you only want to multi-field
update to update the dropdown fields. To do this, you can use the Multiselect.withoutMultiFieldUpdates
wrapper function
within the function which updates the dependent field. It allows you to temporarily turn off the multi-field update within the
scope of its wrapped input function.
Original
Workspace-related
Blocks-related
For more information, please check out the following issue page.
Currently, the keyboard navigation plugin has no compatibility issues with the multiselect plugin. It is enabled by default in the test files. However, one thing that developers and users should note is that the regular Blockly core's copy/cut/paste shortcuts and multiselect plugin's copy/cut/paste shortcuts are completely disabled when the keyboard navigation mode is turned on. This was assumed to be the expected behavior of the user as it defeats the purpose of having the keyboard navigation mode turned on and the user would try to select blocks using the cursor and copy/cut/paste with that selection. However, if a developer wants to allow for either copy/cut/paste behavior (Blockly Core or multiselect plugin) while the keyboard navigation mode is turned on, we would have to make the following changes described in this issue.
The disable top blocks plugin has to be initialized after the multiselect plugin. The main reason behind this is that the multiselect plugin has its own custom context menu, which allows for the disabling of any blocks (not just top blocks). Installing the multiselect plugin after the disable top blocks plugin overrides the disable top blocks plugin's context menu customization. If we install the disable top blocks plugin after the multiselect plugin, everything works as intended.
Multiselect.init
: Initialize the plugin.Multiselect.dispose
: Dispose the plugin.MultiselectDraggable
: The customized draggable object unique to each workspace that contains the blocks in the multiselection.dragSelectionWeakMap
: The WeakMap storing set of currently selected block ids by workspace svg.inMultipleSelectionModeWeakMap
: The WeakMap storing whether the plugin is in multiple selection mode by workspace svg.Multiselect.withoutMultiFieldUpdates
: A wrapper function to ignore multi-field updates.Multiselect.setMultiselectIcon
: Pass in the icon URLs/data to change the multiselect icon at runtime.Apache 2.0