release: Merge branch 'develop' into rc/v11.2.0

This commit is contained in:
Rachel Fenichel
2024-09-04 09:10:56 -07:00
362 changed files with 7675 additions and 9317 deletions

View File

@@ -101,6 +101,8 @@ function buildTSOverride({files, tsconfig}) {
'@typescript-eslint/no-empty-function': ['off'],
// Temporarily disable. 3 problems.
'@typescript-eslint/no-empty-interface': ['off'],
// We use this pattern extensively for block (e.g. controls_if) interfaces.
'@typescript-eslint/no-empty-object-type': ['off'],
// TsDoc rules (using JsDoc plugin)
// Disable built-in jsdoc verifier.

View File

@@ -42,7 +42,7 @@ jobs:
path: _deploy/
- name: Deploy to App Engine
uses: google-github-actions/deploy-appengine@v2.1.0
uses: google-github-actions/deploy-appengine@v2.1.3
# For parameters see:
# https://github.com/google-github-actions/deploy-appengine#inputs
with:

View File

@@ -18,7 +18,7 @@ jobs:
# TODO (#2114): re-enable osx build.
# os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
node-version: [18.x, 20.x]
node-version: [18.x, 20.x, 22.x]
# See supported Node.js release schedule at
# https://nodejs.org/en/about/releases/
@@ -37,8 +37,8 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Npm Install
run: npm install
- name: Npm Clean Install
run: npm ci
- name: Linux Test Setup
if: runner.os == 'Linux'

1
.npmrc
View File

@@ -1 +1,2 @@
registry=https://registry.npmjs.org/
legacy-peer-deps=true

View File

@@ -10,4 +10,6 @@ module.exports = {
bracketSpacing: false,
// Put HTML tag closing brackets on same line as last attribute.
bracketSameLine: true,
// Organise imports using a plugin.
'plugins': ['prettier-plugin-organize-imports'],
};

View File

@@ -6,6 +6,6 @@ var msg = 'Compiled Blockly files should be loaded from https://unpkg.com/blockl
console.log(msg);
try {
alert(msg);
} catch (_e) {
} catch {
// Can't alert? Probably node.js.
}

View File

@@ -6,6 +6,7 @@
// Former goog.module ID: Blockly.libraryBlocks
import type {BlockDefinition} from '../core/blocks.js';
import * as lists from './lists.js';
import * as logic from './logic.js';
import * as loops from './loops.js';
@@ -14,7 +15,6 @@ import * as procedures from './procedures.js';
import * as texts from './text.js';
import * as variables from './variables.js';
import * as variablesDynamic from './variables_dynamic.js';
import type {BlockDefinition} from '../core/blocks.js';
export {
lists,

View File

@@ -6,22 +6,22 @@
// Former goog.module ID: Blockly.libraryBlocks.lists
import * as fieldRegistry from '../core/field_registry.js';
import * as xmlUtils from '../core/utils/xml.js';
import {Align} from '../core/inputs/align.js';
import type {Block} from '../core/block.js';
import type {Connection} from '../core/connection.js';
import type {BlockSvg} from '../core/block_svg.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import {Msg} from '../core/msg.js';
import {MutatorIcon} from '../core/icons/mutator_icon.js';
import type {Workspace} from '../core/workspace.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import type {Connection} from '../core/connection.js';
import '../core/field_dropdown.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import * as fieldRegistry from '../core/field_registry.js';
import {MutatorIcon} from '../core/icons/mutator_icon.js';
import {Align} from '../core/inputs/align.js';
import {ValueInput} from '../core/inputs/value_input.js';
import {Msg} from '../core/msg.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Workspace} from '../core/workspace.js';
/**
* A dictionary of the block definitions provided by this module.

View File

@@ -6,22 +6,22 @@
// Former goog.module ID: Blockly.libraryBlocks.logic
import * as Events from '../core/events/events.js';
import * as Extensions from '../core/extensions.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import type {Block} from '../core/block.js';
import type {BlockSvg} from '../core/block_svg.js';
import type {Connection} from '../core/connection.js';
import {Msg} from '../core/msg.js';
import type {Workspace} from '../core/workspace.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import type {Connection} from '../core/connection.js';
import * as Events from '../core/events/events.js';
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import * as Extensions from '../core/extensions.js';
import '../core/field_dropdown.js';
import '../core/field_label.js';
import '../core/icons/mutator_icon.js';
import {Msg} from '../core/msg.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Workspace} from '../core/workspace.js';
/**
* A dictionary of the block definitions provided by this module.

View File

@@ -6,27 +6,27 @@
// Former goog.module ID: Blockly.libraryBlocks.loops
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import type {Block} from '../core/block.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import * as ContextMenu from '../core/contextmenu.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import * as Events from '../core/events/events.js';
import * as Extensions from '../core/extensions.js';
import {Msg} from '../core/msg.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import * as eventUtils from '../core/events/utils.js';
import * as Extensions from '../core/extensions.js';
import '../core/field_dropdown.js';
import '../core/field_label.js';
import '../core/field_number.js';
import '../core/field_variable.js';
import '../core/icons/warning_icon.js';
import {FieldVariable} from '../core/field_variable.js';
import '../core/icons/warning_icon.js';
import {Msg} from '../core/msg.js';
import {WorkspaceSvg} from '../core/workspace_svg.js';
/**

View File

@@ -6,18 +6,18 @@
// Former goog.module ID: Blockly.libraryBlocks.math
import * as Extensions from '../core/extensions.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Block} from '../core/block.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import * as Extensions from '../core/extensions.js';
import '../core/field_dropdown.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import '../core/field_label.js';
import '../core/field_number.js';
import '../core/field_variable.js';
import * as xmlUtils from '../core/utils/xml.js';
/**
* A dictionary of the block definitions provided by this module.

View File

@@ -6,40 +6,40 @@
// Former goog.module ID: Blockly.libraryBlocks.procedures
import * as ContextMenu from '../core/contextmenu.js';
import * as Events from '../core/events/events.js';
import * as Procedures from '../core/procedures.js';
import * as Variables from '../core/variables.js';
import * as Xml from '../core/xml.js';
import * as fieldRegistry from '../core/field_registry.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import {Align} from '../core/inputs/align.js';
import type {Block} from '../core/block.js';
import type {BlockSvg} from '../core/block_svg.js';
import type {BlockCreate} from '../core/events/events_block_create.js';
import type {BlockChange} from '../core/events/events_block_change.js';
import type {BlockDefinition} from '../core/blocks.js';
import * as common from '../core/common.js';
import {defineBlocks} from '../core/common.js';
import {config} from '../core/config.js';
import type {Connection} from '../core/connection.js';
import * as ContextMenu from '../core/contextmenu.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import * as Events from '../core/events/events.js';
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import type {BlockChange} from '../core/events/events_block_change.js';
import type {BlockCreate} from '../core/events/events_block_create.js';
import * as eventUtils from '../core/events/utils.js';
import {FieldCheckbox} from '../core/field_checkbox.js';
import {FieldLabel} from '../core/field_label.js';
import * as fieldRegistry from '../core/field_registry.js';
import {FieldTextInput} from '../core/field_textinput.js';
import {Msg} from '../core/msg.js';
import '../core/icons/comment_icon.js';
import {MutatorIcon as Mutator} from '../core/icons/mutator_icon.js';
import '../core/icons/warning_icon.js';
import {Align} from '../core/inputs/align.js';
import {Msg} from '../core/msg.js';
import {Names} from '../core/names.js';
import * as Procedures from '../core/procedures.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {VariableModel} from '../core/variable_model.js';
import * as Variables from '../core/variables.js';
import type {Workspace} from '../core/workspace.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
import {config} from '../core/config.js';
import {defineBlocks} from '../core/common.js';
import '../core/icons/comment_icon.js';
import '../core/icons/warning_icon.js';
import * as common from '../core/common.js';
import * as Xml from '../core/xml.js';
/** A dictionary of the block definitions provided by this module. */
export const blocks: {[key: string]: BlockDefinition} = {};
@@ -1206,7 +1206,7 @@ blocks['procedures_callreturn'] = {
this.appendDummyInput('TOPROW').appendField('', 'NAME');
this.setOutput(true);
this.setStyle('procedure_blocks');
// Tooltip is set in domToMutation.
// Tooltip is set in renameProcedure.
this.setHelpUrl(Msg['PROCEDURES_CALLRETURN_HELPURL']);
this.arguments_ = [];
this.argumentVarModels_ = [];

View File

@@ -6,25 +6,25 @@
// Former goog.module ID: Blockly.libraryBlocks.texts
import * as Extensions from '../core/extensions.js';
import * as fieldRegistry from '../core/field_registry.js';
import * as xmlUtils from '../core/utils/xml.js';
import {Align} from '../core/inputs/align.js';
import type {Block} from '../core/block.js';
import type {BlockSvg} from '../core/block_svg.js';
import {Connection} from '../core/connection.js';
import {FieldImage} from '../core/field_image.js';
import {FieldDropdown} from '../core/field_dropdown.js';
import {FieldTextInput} from '../core/field_textinput.js';
import {Msg} from '../core/msg.js';
import {MutatorIcon} from '../core/icons/mutator_icon.js';
import type {Workspace} from '../core/workspace.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import {Connection} from '../core/connection.js';
import * as Extensions from '../core/extensions.js';
import {FieldDropdown} from '../core/field_dropdown.js';
import {FieldImage} from '../core/field_image.js';
import * as fieldRegistry from '../core/field_registry.js';
import {FieldTextInput} from '../core/field_textinput.js';
import '../core/field_variable.js';
import {MutatorIcon} from '../core/icons/mutator_icon.js';
import {Align} from '../core/inputs/align.js';
import {ValueInput} from '../core/inputs/value_input.js';
import {Msg} from '../core/msg.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Workspace} from '../core/workspace.js';
/**
* A dictionary of the block definitions provided by this module.
@@ -438,6 +438,11 @@ const PROMPT_COMMON = {
domToMutation: function (this: PromptCommonBlock, xmlElement: Element) {
this.updateType_(xmlElement.getAttribute('type')!);
},
// These blocks do not need JSO serialization hooks (saveExtraState
// and loadExtraState) because the state of this object is already
// encoded in the dropdown values.
// XML hooks are kept for backwards compatibility.
};
blocks['text_prompt_ext'] = {
@@ -468,16 +473,11 @@ blocks['text_prompt_ext'] = {
: Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
});
},
// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this object is already encoded in the
// dropdown values.
// XML hooks are kept for backwards compatibility.
};
type PromptBlock = Block & PromptCommonMixin & QuoteImageMixin;
const TEXT_PROMPT_BLOCK = {
blocks['text_prompt'] = {
...PROMPT_COMMON,
/**
* Block for prompt function (internal message).
@@ -520,8 +520,6 @@ const TEXT_PROMPT_BLOCK = {
},
};
blocks['text_prompt'] = TEXT_PROMPT_BLOCK;
blocks['text_count'] = {
/**
* Block for counting how many times one string appears within another string.
@@ -666,7 +664,7 @@ const QUOTE_IMAGE_MIXIN = {
* closing double quote. The selected quote will be adapted for RTL blocks.
*
* @param open If the image should be open quote (“ in LTR).
* Otherwise, a closing quote is used (” in LTR).
* Otherwise, a closing quote is used (” in LTR).
* @returns The new field.
*/
newQuote_: function (this: QuoteImageBlock, open: boolean): FieldImage {

View File

@@ -6,22 +6,22 @@
// Former goog.module ID: Blockly.libraryBlocks.variables
import * as ContextMenu from '../core/contextmenu.js';
import * as Extensions from '../core/extensions.js';
import * as Variables from '../core/variables.js';
import type {Block} from '../core/block.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import {FieldVariable} from '../core/field_variable.js';
import {Msg} from '../core/msg.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import * as ContextMenu from '../core/contextmenu.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import * as Extensions from '../core/extensions.js';
import '../core/field_label.js';
import {FieldVariable} from '../core/field_variable.js';
import {Msg} from '../core/msg.js';
import * as Variables from '../core/variables.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
/**
* A dictionary of the block definitions provided by this module.

View File

@@ -6,23 +6,23 @@
// Former goog.module ID: Blockly.libraryBlocks.variablesDynamic
import * as ContextMenu from '../core/contextmenu.js';
import * as Extensions from '../core/extensions.js';
import * as Variables from '../core/variables.js';
import {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import type {Block} from '../core/block.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import {FieldVariable} from '../core/field_variable.js';
import {Msg} from '../core/msg.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import * as ContextMenu from '../core/contextmenu.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import * as Extensions from '../core/extensions.js';
import '../core/field_label.js';
import {FieldVariable} from '../core/field_variable.js';
import {Msg} from '../core/msg.js';
import * as Variables from '../core/variables.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
/**
* A dictionary of the block definitions provided by this module.

View File

@@ -23,35 +23,36 @@ import * as common from './common.js';
import {Connection} from './connection.js';
import {ConnectionType} from './connection_type.js';
import * as constants from './constants.js';
import {DuplicateIconType} from './icons/exceptions.js';
import type {Abstract} from './events/events_abstract.js';
import type {BlockChange} from './events/events_block_change.js';
import type {BlockMove} from './events/events_block_move.js';
import * as deprecation from './utils/deprecation.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import * as Extensions from './extensions.js';
import type {Field} from './field.js';
import * as fieldRegistry from './field_registry.js';
import {Input} from './inputs/input.js';
import {Align} from './inputs/align.js';
import type {IASTNodeLocation} from './interfaces/i_ast_node_location.js';
import {type IIcon} from './interfaces/i_icon.js';
import {isCommentIcon} from './interfaces/i_comment_icon.js';
import {DuplicateIconType} from './icons/exceptions.js';
import {IconType} from './icons/icon_types.js';
import type {MutatorIcon} from './icons/mutator_icon.js';
import {Align} from './inputs/align.js';
import {DummyInput} from './inputs/dummy_input.js';
import {EndRowInput} from './inputs/end_row_input.js';
import {Input} from './inputs/input.js';
import {StatementInput} from './inputs/statement_input.js';
import {ValueInput} from './inputs/value_input.js';
import type {IASTNodeLocation} from './interfaces/i_ast_node_location.js';
import {isCommentIcon} from './interfaces/i_comment_icon.js';
import {type IIcon} from './interfaces/i_icon.js';
import * as registry from './registry.js';
import * as Tooltip from './tooltip.js';
import * as arrayUtils from './utils/array.js';
import {Coordinate} from './utils/coordinate.js';
import * as deprecation from './utils/deprecation.js';
import * as idGenerator from './utils/idgenerator.js';
import * as parsing from './utils/parsing.js';
import * as registry from './registry.js';
import {Size} from './utils/size.js';
import type {VariableModel} from './variable_model.js';
import type {Workspace} from './workspace.js';
import {DummyInput} from './inputs/dummy_input.js';
import {EndRowInput} from './inputs/end_row_input.js';
import {ValueInput} from './inputs/value_input.js';
import {StatementInput} from './inputs/statement_input.js';
import {IconType} from './icons/icon_types.js';
/**
* Class for one block.
@@ -143,24 +144,31 @@ export class Block implements IASTNodeLocation {
suppressPrefixSuffix: boolean | null = false;
/**
* An optional property for declaring developer variables. Return a list of
* variable names for use by generators. Developer variables are never
* shown to the user, but are declared as global variables in the generated
* code.
* An optional method for declaring developer variables, to be used
* by generators. Developer variables are never shown to the user,
* but are declared as global variables in the generated code.
*
* @returns a list of developer variable names.
*/
getDeveloperVariables?: () => string[];
/**
* An optional function that reconfigures the block based on the contents of
* the mutator dialog.
* An optional method that reconfigures the block based on the
* contents of the mutator dialog.
*
* @param rootBlock The root block in the mutator flyout.
*/
compose?: (p1: Block) => void;
compose?: (rootBlock: Block) => void;
/**
* An optional function that populates the mutator's dialog with
* this block's components.
* An optional function that populates the mutator flyout with
* blocks representing this block's configuration.
*
* @param workspace The mutator flyout's workspace.
* @returns The root block created in the flyout's workspace.
*/
decompose?: (p1: Workspace) => Block;
decompose?: (workspace: Workspace) => Block;
id: string;
outputConnection: Connection | null = null;
nextConnection: Connection | null = null;
@@ -216,7 +224,7 @@ export class Block implements IASTNodeLocation {
/**
* String for block help, or function that returns a URL. Null for no help.
*/
helpUrl: string | Function | null = null;
helpUrl: string | (() => string) | null = null;
/** A bound callback function to use when the parent workspace changes. */
private onchangeWrapper_: ((p1: Abstract) => void) | null = null;
@@ -297,7 +305,7 @@ export class Block implements IASTNodeLocation {
// Fire a create event.
if (eventUtils.isEnabled()) {
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(this));
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_CREATE))(this));
}
} finally {
eventUtils.setGroup(existingGroup);
@@ -332,7 +340,7 @@ export class Block implements IASTNodeLocation {
this.unplug(healStack);
if (eventUtils.isEnabled()) {
// Constructing the delete event is costly. Only perform if necessary.
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_DELETE))(this));
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_DELETE))(this));
}
this.workspace.removeTopBlock(this);
this.disposeInternal();
@@ -716,7 +724,7 @@ export class Block implements IASTNodeLocation {
}
// Check that block is connected to new parent if new parent is not null and
// that block is not connected to superior one if new parent is null.
// that block is not connected to superior one if new parent is null.
const targetBlock =
(this.previousConnection && this.previousConnection.targetBlock()) ||
(this.outputConnection && this.outputConnection.targetBlock());
@@ -734,14 +742,13 @@ export class Block implements IASTNodeLocation {
}
// This block hasn't actually moved on-screen, so there's no need to
// update
// its connection locations.
// update its connection locations.
if (this.parentBlock_) {
// Remove this block from the old parent's child list.
arrayUtils.removeElem(this.parentBlock_.childBlocks_, this);
} else {
// New parent must be non-null so remove this block from the workspace's
// list of top-most blocks.
// New parent must be non-null so remove this block from the
// workspace's list of top-most blocks.
this.workspace.removeTopBlock(this);
}
@@ -991,7 +998,7 @@ export class Block implements IASTNodeLocation {
* @param url URL string for block help, or function that returns a URL. Null
* for no help.
*/
setHelpUrl(url: string | Function) {
setHelpUrl(url: string | (() => string)) {
this.helpUrl = url;
}
@@ -1323,7 +1330,7 @@ export class Block implements IASTNodeLocation {
setInputsInline(newBoolean: boolean) {
if (this.inputsInline !== newBoolean) {
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this,
'inline',
null,
@@ -1460,13 +1467,32 @@ export class Block implements IASTNodeLocation {
* update whether the block is currently disabled for this reason.
*/
setDisabledReason(disabled: boolean, reason: string): void {
// Workspaces that were serialized before the reason for being disabled
// could be specified may have blocks that are disabled without a known
// reason. On being loaded, these blocks will default to having the manually
// disabled reason. However, if the user isn't allowed to manually disable
// or enable blocks, then this manually disabled reason cannot be removed.
// For backward compatibility with these legacy workspaces, when removing
// any disabled reason and the workspace does not allow manually disabling
// but the block is manually disabled, then remove the manually disabled
// reason in addition to the more specific reason. For example, when an
// orphaned block is no longer orphaned, the block should be enabled again.
if (
!disabled &&
!this.workspace.options.disable &&
this.hasDisabledReason(constants.MANUALLY_DISABLED) &&
reason != constants.MANUALLY_DISABLED
) {
this.setDisabledReason(false, constants.MANUALLY_DISABLED);
}
if (this.disabledReasons.has(reason) !== disabled) {
if (disabled) {
this.disabledReasons.add(reason);
} else {
this.disabledReasons.delete(reason);
}
const blockChangeEvent = new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
const blockChangeEvent = new (eventUtils.get(EventType.BLOCK_CHANGE))(
this,
'disabled',
/* name= */ null,
@@ -1534,7 +1560,7 @@ export class Block implements IASTNodeLocation {
setCollapsed(collapsed: boolean) {
if (this.collapsed_ !== collapsed) {
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this,
'collapsed',
null,
@@ -1839,7 +1865,7 @@ export class Block implements IASTNodeLocation {
const rawValue = json['colour'];
try {
this.setColour(rawValue);
} catch (e) {
} catch {
console.warn(warningPrefix + 'Illegal colour value: ', rawValue);
}
}
@@ -1856,7 +1882,7 @@ export class Block implements IASTNodeLocation {
const blockStyleName = json['style'];
try {
this.setStyle(blockStyleName);
} catch (styleError) {
} catch {
console.warn(warningPrefix + 'Style does not exist: ', blockStyleName);
}
}
@@ -2333,7 +2359,7 @@ export class Block implements IASTNodeLocation {
}
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this,
'comment',
null,
@@ -2433,10 +2459,8 @@ export class Block implements IASTNodeLocation {
if (this.parentBlock_) {
throw Error('Block has parent');
}
const event = new (eventUtils.get(eventUtils.BLOCK_MOVE))(
this,
) as BlockMove;
reason && event.setReason(reason);
const event = new (eventUtils.get(EventType.BLOCK_MOVE))(this) as BlockMove;
if (reason) event.setReason(reason);
this.xy_.translate(dx, dy);
event.recordNew();
eventUtils.fire(event);

View File

@@ -16,7 +16,9 @@ import './events/events_selected.js';
import {Block} from './block.js';
import * as blockAnimations from './block_animations.js';
import {IDeletable} from './blockly.js';
import * as browserEvents from './browser_events.js';
import {BlockCopyData, BlockPaster} from './clipboard/block_paster.js';
import * as common from './common.js';
import {config} from './config.js';
import type {Connection} from './connection.js';
@@ -28,11 +30,15 @@ import {
ContextMenuRegistry,
LegacyContextMenuOption,
} from './contextmenu_registry.js';
import {BlockDragStrategy} from './dragging/block_drag_strategy.js';
import type {BlockMove} from './events/events_block_move.js';
import * as deprecation from './utils/deprecation.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import type {Field} from './field.js';
import {FieldLabel} from './field_label.js';
import {IconType} from './icons/icon_types.js';
import {MutatorIcon} from './icons/mutator_icon.js';
import {WarningIcon} from './icons/warning_icon.js';
import type {Input} from './inputs/input.js';
import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js';
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
@@ -44,26 +50,21 @@ import {ASTNode} from './keyboard_nav/ast_node.js';
import {TabNavigateCursor} from './keyboard_nav/tab_navigate_cursor.js';
import {MarkerManager} from './marker_manager.js';
import {Msg} from './msg.js';
import {MutatorIcon} from './icons/mutator_icon.js';
import * as renderManagement from './render_management.js';
import {RenderedConnection} from './rendered_connection.js';
import type {IPathObject} from './renderers/common/i_path_object.js';
import * as blocks from './serialization/blocks.js';
import type {BlockStyle} from './theme.js';
import * as Tooltip from './tooltip.js';
import {Coordinate} from './utils/coordinate.js';
import * as deprecation from './utils/deprecation.js';
import * as dom from './utils/dom.js';
import {Rect} from './utils/rect.js';
import {Svg} from './utils/svg.js';
import * as svgMath from './utils/svg_math.js';
import {WarningIcon} from './icons/warning_icon.js';
import {FlyoutItemInfo} from './utils/toolbox.js';
import type {Workspace} from './workspace.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as renderManagement from './render_management.js';
import {IconType} from './icons/icon_types.js';
import {BlockCopyData, BlockPaster} from './clipboard/block_paster.js';
import {BlockDragStrategy} from './dragging/block_drag_strategy.js';
import {IDeletable} from './blockly.js';
import {FlyoutItemInfo} from './utils/toolbox.js';
/**
* Class for a block's SVG representation.
@@ -92,7 +93,25 @@ export class BlockSvg
static readonly COLLAPSED_WARNING_ID = 'TEMP_COLLAPSED_WARNING_';
override decompose?: (p1: Workspace) => BlockSvg;
// override compose?: ((p1: BlockSvg) => void)|null;
saveConnections?: (p1: BlockSvg) => void;
/**
* An optional method which saves a record of blocks connected to
* this block so they can be later restored after this block is
* recoomposed (reconfigured). Typically records the connected
* blocks on properties on blocks in the mutator flyout, so that
* rearranging those component blocks will automatically rearrange
* the corresponding connected blocks on this block after this block
* is recomposed.
*
* To keep the saved connection information up-to-date, MutatorIcon
* arranges for an event listener to call this method any time the
* mutator flyout is open and a change occurs on this block's
* workspace.
*
* @param rootBlock The root block in the mutator flyout.
*/
saveConnections?: (rootBlock: BlockSvg) => void;
customContextMenu?: (
p1: Array<ContextMenuOption | LegacyContextMenuOption>,
) => void;
@@ -109,6 +128,14 @@ export class BlockSvg
*/
width = 0;
/**
* Width of this block, not including any connected value blocks.
* Width is in workspace units.
*
* @internal
*/
childlessWidth = 0;
/**
* Map from IDs for warnings text to PIDs of functions to apply them.
* Used to be able to maintain multiple warnings.
@@ -252,7 +279,7 @@ export class BlockSvg
this.addSelect();
}
/** Unselects this block. Unhighlights the blockv visually. */
/** Unselects this block. Unhighlights the block visually. */
unselect() {
if (this.isShadow()) {
this.getParent()?.unselect();
@@ -351,8 +378,8 @@ export class BlockSvg
const eventsEnabled = eventUtils.isEnabled();
let event: BlockMove | null = null;
if (eventsEnabled) {
event = new (eventUtils.get(eventUtils.BLOCK_MOVE)!)(this) as BlockMove;
reason && event.setReason(reason);
event = new (eventUtils.get(EventType.BLOCK_MOVE)!)(this) as BlockMove;
if (reason) event.setReason(reason);
}
const delta = new Coordinate(dx, dy);
@@ -436,8 +463,28 @@ export class BlockSvg
* @returns Object with coordinates of the bounding box.
*/
getBoundingRectangle(): Rect {
return this.getBoundingRectangleWithDimensions(this.getHeightWidth());
}
/**
* Returns the coordinates of a bounding box describing the dimensions of this
* block alone.
* Coordinate system: workspace coordinates.
*
* @returns Object with coordinates of the bounding box.
*/
getBoundingRectangleWithoutChildren(): Rect {
return this.getBoundingRectangleWithDimensions({
height: this.height,
width: this.childlessWidth,
});
}
private getBoundingRectangleWithDimensions(blockBounds: {
height: number;
width: number;
}) {
const blockXY = this.getRelativeToSurfaceXY();
const blockBounds = this.getHeightWidth();
let left;
let right;
if (this.RTL) {
@@ -750,6 +797,25 @@ export class BlockSvg
blockAnimations.disposeUiEffect(this);
}
// Selecting a shadow block highlights an ancestor block, but that highlight
// should be removed if the shadow block will be deleted. So, before
// deleting blocks and severing the connections between them, check whether
// doing so would delete a selected block and make sure that any associated
// parent is updated.
const selection = common.getSelected();
if (selection instanceof Block) {
let selectionAncestor: Block | null = selection;
while (selectionAncestor !== null) {
if (selectionAncestor === this) {
// The block to be deleted contains the selected block, so remove any
// selection highlight associated with the selected block before
// deleting them.
selection.unselect();
}
selectionAncestor = selectionAncestor.getParent();
}
}
super.dispose(!!healStack);
dom.removeNode(this.svgGroup_);
}
@@ -1125,10 +1191,11 @@ export class BlockSvg
* <g> tags do not respect z-index so SVG renders them in the
* order that they are in the DOM. By placing this block first within the
* block group's <g>, it will render on top of any other blocks.
* Use sparingly, this method is expensive because it reorders the DOM
* nodes.
*
* @param blockOnly: True to only move this block to the front without
* @param blockOnly True to only move this block to the front without
* adjusting its parents.
* @internal
*/
bringToFront(blockOnly = false) {
/* eslint-disable-next-line @typescript-eslint/no-this-alias */

View File

@@ -22,8 +22,10 @@ import {BlocklyOptions} from './blockly_options.js';
import {Blocks} from './blocks.js';
import * as browserEvents from './browser_events.js';
import * as bubbles from './bubbles.js';
import {MiniWorkspaceBubble} from './bubbles/mini_workspace_bubble.js';
import * as bumpObjects from './bump_objects.js';
import * as clipboard from './clipboard.js';
import * as comments from './comments.js';
import * as common from './common.js';
import {ComponentManager} from './component_manager.js';
import {config} from './config.js';
@@ -31,15 +33,15 @@ import {Connection} from './connection.js';
import {ConnectionChecker} from './connection_checker.js';
import {ConnectionDB} from './connection_db.js';
import {ConnectionType} from './connection_type.js';
import * as constants from './constants.js';
import * as ContextMenu from './contextmenu.js';
import * as ContextMenuItems from './contextmenu_items.js';
import {ContextMenuRegistry} from './contextmenu_registry.js';
import * as comments from './comments.js';
import * as Css from './css.js';
import {DeleteArea} from './delete_area.js';
import * as dialog from './dialog.js';
import * as dragging from './dragging.js';
import {DragTarget} from './drag_target.js';
import * as dragging from './dragging.js';
import * as dropDownDiv from './dropdowndiv.js';
import * as Events from './events/events.js';
import * as Extensions from './extensions.js';
@@ -104,8 +106,8 @@ import {Gesture} from './gesture.js';
import {Grid} from './grid.js';
import * as icons from './icons.js';
import {inject} from './inject.js';
import {Input} from './inputs/input.js';
import * as inputs from './inputs.js';
import {Input} from './inputs/input.js';
import {InsertionMarkerManager} from './insertion_marker_manager.js';
import {InsertionMarkerPreviewer} from './insertion_marker_previewer.js';
import {IASTNodeLocation} from './interfaces/i_ast_node_location.js';
@@ -119,16 +121,16 @@ import {IComponent} from './interfaces/i_component.js';
import {IConnectionChecker} from './interfaces/i_connection_checker.js';
import {IConnectionPreviewer} from './interfaces/i_connection_previewer.js';
import {IContextMenu} from './interfaces/i_contextmenu.js';
import {ICopyable, isCopyable, ICopyData} from './interfaces/i_copyable.js';
import {ICopyData, ICopyable, isCopyable} from './interfaces/i_copyable.js';
import {IDeletable, isDeletable} from './interfaces/i_deletable.js';
import {IDeleteArea} from './interfaces/i_delete_area.js';
import {IDragTarget} from './interfaces/i_drag_target.js';
import {IDragger} from './interfaces/i_dragger.js';
import {
IDragStrategy,
IDraggable,
isDraggable,
IDragStrategy,
} from './interfaces/i_draggable.js';
import {IDragger} from './interfaces/i_dragger.js';
import {IFlyout} from './interfaces/i_flyout.js';
import {IHasBubble, hasBubble} from './interfaces/i_has_bubble.js';
import {IIcon, isIcon} from './interfaces/i_icon.js';
@@ -159,22 +161,21 @@ import {BasicCursor} from './keyboard_nav/basic_cursor.js';
import {Cursor} from './keyboard_nav/cursor.js';
import {Marker} from './keyboard_nav/marker.js';
import {TabNavigateCursor} from './keyboard_nav/tab_navigate_cursor.js';
import {MarkerManager} from './marker_manager.js';
import type {LayerManager} from './layer_manager.js';
import * as layers from './layers.js';
import {MarkerManager} from './marker_manager.js';
import {Menu} from './menu.js';
import {MenuItem} from './menuitem.js';
import {MetricsManager} from './metrics_manager.js';
import {Msg, setLocale} from './msg.js';
import {MiniWorkspaceBubble} from './bubbles/mini_workspace_bubble.js';
import {Names} from './names.js';
import {Options} from './options.js';
import * as uiPosition from './positionable_helpers.js';
import * as Procedures from './procedures.js';
import * as registry from './registry.js';
import {RenderedConnection} from './rendered_connection.js';
import * as renderManagement from './render_management.js';
import {RenderedConnection} from './rendered_connection.js';
import * as blockRendering from './renderers/common/block_rendering.js';
import * as constants from './constants.js';
import * as geras from './renderers/geras/geras.js';
import * as thrasos from './renderers/thrasos/thrasos.js';
import * as zelos from './renderers/zelos/zelos.js';
@@ -417,171 +418,187 @@ Names.prototype.populateProcedures = function (
// clang-format on
// Re-export submodules that no longer declareLegacyNamespace.
export {browserEvents};
export {ContextMenu};
export {ContextMenuItems};
export {Css};
export {Events};
export {Extensions};
export {Procedures};
export {Procedures as procedures};
export {ShortcutItems};
export {Themes};
export {Tooltip};
export {Touch};
export {Variables};
export {VariablesDynamic};
export {WidgetDiv};
export {Xml};
export {blockAnimations};
export {blockRendering};
export {bumpObjects};
export {clipboard};
export {common};
export {constants};
export {dialog};
export {fieldRegistry};
export {geras};
export {registry};
export {thrasos};
export {uiPosition};
export {utils};
export {zelos};
export {ASTNode};
export {BasicCursor};
export {Block};
export {BlocklyOptions};
export {BlockSvg};
export {Blocks};
export {bubbles};
export {CollapsibleToolboxCategory};
export {ComponentManager};
export {Connection};
export {ConnectionType};
export {ConnectionChecker};
export {ConnectionDB};
export {ContextMenuRegistry};
export {comments};
export {Cursor};
export {DeleteArea};
export {dragging};
export {DragTarget};
export const DropDownDiv = dropDownDiv;
export {Field, FieldConfig, FieldValidator, UnattachedFieldError};
export {
ASTNode,
BasicCursor,
Block,
BlockSvg,
BlocklyOptions,
Blocks,
CollapsibleToolboxCategory,
ComponentManager,
Connection,
ConnectionChecker,
ConnectionDB,
ConnectionType,
ContextMenu,
ContextMenuItems,
ContextMenuRegistry,
Css,
Cursor,
DeleteArea,
DragTarget,
Events,
Extensions,
Procedures,
ShortcutItems,
Themes,
Tooltip,
Touch,
Variables,
VariablesDynamic,
WidgetDiv,
Xml,
blockAnimations,
blockRendering,
browserEvents,
bubbles,
bumpObjects,
clipboard,
comments,
common,
constants,
dialog,
dragging,
fieldRegistry,
geras,
Procedures as procedures,
registry,
thrasos,
uiPosition,
utils,
zelos,
};
export const DropDownDiv = dropDownDiv;
export {
CodeGenerator,
Field,
FieldCheckbox,
FieldCheckboxConfig,
FieldCheckboxFromJsonConfig,
FieldCheckboxValidator,
};
export {
FieldConfig,
FieldDropdown,
FieldDropdownConfig,
FieldDropdownFromJsonConfig,
FieldDropdownValidator,
MenuGenerator,
MenuGeneratorFunction,
MenuOption,
};
export {FieldImage, FieldImageConfig, FieldImageFromJsonConfig};
export {FieldLabel, FieldLabelConfig, FieldLabelFromJsonConfig};
export {FieldLabelSerializable};
export {
FieldImage,
FieldImageConfig,
FieldImageFromJsonConfig,
FieldLabel,
FieldLabelConfig,
FieldLabelFromJsonConfig,
FieldLabelSerializable,
FieldNumber,
FieldNumberConfig,
FieldNumberFromJsonConfig,
FieldNumberValidator,
};
export {
FieldTextInput,
FieldTextInputConfig,
FieldTextInputFromJsonConfig,
FieldTextInputValidator,
};
export {
FieldValidator,
FieldVariable,
FieldVariableConfig,
FieldVariableFromJsonConfig,
FieldVariableValidator,
Flyout,
FlyoutButton,
FlyoutMetricsManager,
CodeGenerator as Generator,
Gesture,
Grid,
HorizontalFlyout,
IASTNodeLocation,
IASTNodeLocationSvg,
IASTNodeLocationWithBlock,
IAutoHideable,
IBoundedElement,
IBubble,
ICollapsibleToolboxItem,
IComponent,
IConnectionChecker,
IConnectionPreviewer,
IContextMenu,
ICopyData,
ICopyable,
IDeletable,
IDeleteArea,
IDragStrategy,
IDragTarget,
IDraggable,
IDragger,
IFlyout,
IHasBubble,
IIcon,
IKeyboardAccessible,
IMetricsManager,
IMovable,
IObservable,
IPaster,
IPositionable,
IRegistrable,
IRenderedElement,
ISelectable,
ISelectableToolboxItem,
ISerializable,
IStyleable,
IToolbox,
IToolboxItem,
IVariableBackedParameterModel,
Input,
InsertionMarkerManager,
InsertionMarkerPreviewer,
LayerManager,
Marker,
MarkerManager,
Menu,
MenuGenerator,
MenuGeneratorFunction,
MenuItem,
MenuOption,
MetricsManager,
Msg,
Names,
Options,
RenderedConnection,
Scrollbar,
ScrollbarPair,
ShortcutRegistry,
TabNavigateCursor,
Theme,
ThemeManager,
Toolbox,
ToolboxCategory,
ToolboxItem,
ToolboxSeparator,
Trashcan,
UnattachedFieldError,
VariableMap,
VariableModel,
VerticalFlyout,
Workspace,
WorkspaceAudio,
WorkspaceDragger,
WorkspaceSvg,
ZoomControls,
config,
hasBubble,
icons,
inject,
inputs,
isCopyable,
isDeletable,
isDraggable,
isIcon,
isObservable,
isPaster,
isRenderedElement,
isSelectable,
isSerializable,
isVariableBackedParameterModel,
layers,
renderManagement,
serialization,
setLocale,
};
export {Flyout};
export {FlyoutButton};
export {FlyoutMetricsManager};
export {CodeGenerator};
export {CodeGenerator as Generator}; // Deprecated name, October 2022.
export {Gesture};
export {Grid};
export {HorizontalFlyout};
export {IASTNodeLocation};
export {IASTNodeLocationSvg};
export {IASTNodeLocationWithBlock};
export {IAutoHideable};
export {IBoundedElement};
export {IBubble};
export {ICollapsibleToolboxItem};
export {IComponent};
export {IConnectionChecker};
export {IConnectionPreviewer};
export {IContextMenu};
export {icons};
export {ICopyable, isCopyable, ICopyData};
export {IDeletable, isDeletable};
export {IDeleteArea};
export {IDragTarget};
export {IDragger};
export {IDraggable, isDraggable, IDragStrategy};
export {IFlyout};
export {IHasBubble, hasBubble};
export {IIcon, isIcon};
export {IKeyboardAccessible};
export {IMetricsManager};
export {IMovable};
export {Input};
export {inputs};
export {InsertionMarkerManager};
export {InsertionMarkerPreviewer};
export {IObservable, isObservable};
export {IPaster, isPaster};
export {IPositionable};
export {IRegistrable};
export {IRenderedElement, isRenderedElement};
export {ISelectable, isSelectable};
export {ISelectableToolboxItem};
export {ISerializable, isSerializable};
export {IStyleable};
export {IToolbox};
export {IToolboxItem};
export {IVariableBackedParameterModel, isVariableBackedParameterModel};
export {Marker};
export {MarkerManager};
export {LayerManager};
export {Menu};
export {MenuItem};
export {MetricsManager};
export {Msg, setLocale};
export {Names};
export {Options};
export {RenderedConnection};
export {renderManagement};
export {Scrollbar};
export {ScrollbarPair};
export {ShortcutRegistry};
export {TabNavigateCursor};
export {Theme};
export {ThemeManager};
export {Toolbox};
export {ToolboxCategory};
export {ToolboxItem};
export {ToolboxSeparator};
export {Trashcan};
export {VariableMap};
export {VariableModel};
export {VerticalFlyout};
export {Workspace};
export {WorkspaceAudio};
export {WorkspaceDragger};
export {WorkspaceSvg};
export {ZoomControls};
export {config};
export {inject};
export {serialization};

View File

@@ -6,9 +6,9 @@
// Former goog.module ID: Blockly.BlocklyOptions
import type {Theme, ITheme} from './theme.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import type {ITheme, Theme} from './theme.js';
import type {ToolboxDefinition} from './utils/toolbox.js';
import type {WorkspaceSvg} from './workspace_svg.js';
/**
* Blockly options.

View File

@@ -6,6 +6,10 @@
// Former goog.module ID: Blockly.browserEvents
// Theoretically we could figure out a way to type the event params correctly,
// but it's not high priority.
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
import * as Touch from './touch.js';
import * as userAgent from './utils/useragent.js';
@@ -47,7 +51,7 @@ const PAGE_MODE_MULTIPLIER = 125;
export function conditionalBind(
node: EventTarget,
name: string,
thisObject: Object | null,
thisObject: object | null,
func: Function,
opt_noCaptureIdentifier?: boolean,
): Data {
@@ -96,7 +100,7 @@ export function conditionalBind(
export function bind(
node: EventTarget,
name: string,
thisObject: Object | null,
thisObject: object | null,
func: Function,
): Data {
/**

View File

@@ -5,8 +5,8 @@
*/
import {Bubble} from './bubbles/bubble.js';
import {MiniWorkspaceBubble} from './bubbles/mini_workspace_bubble.js';
import {TextBubble} from './bubbles/text_bubble.js';
import {TextInputBubble} from './bubbles/textinput_bubble.js';
import {MiniWorkspaceBubble} from './bubbles/mini_workspace_bubble.js';
export {Bubble, TextBubble, TextInputBubble, MiniWorkspaceBubble};
export {Bubble, MiniWorkspaceBubble, TextBubble, TextInputBubble};

View File

@@ -4,21 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {ISelectable} from '../blockly.js';
import * as browserEvents from '../browser_events.js';
import * as common from '../common.js';
import {BubbleDragStrategy} from '../dragging/bubble_drag_strategy.js';
import {IBubble} from '../interfaces/i_bubble.js';
import {ContainerRegion} from '../metrics_manager.js';
import {Scrollbar} from '../scrollbar.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import * as idGenerator from '../utils/idgenerator.js';
import * as math from '../utils/math.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import * as common from '../common.js';
import {ISelectable} from '../blockly.js';
import * as idGenerator from '../utils/idgenerator.js';
/**
* The abstract pop-up bubble class. This creates a UI that looks like a speech
@@ -212,9 +212,10 @@ export abstract class Bubble implements IBubble, ISelectable {
this.background.setAttribute('fill', colour);
}
/** Passes the pointer event off to the gesture system. */
/** Brings the bubble to the front and passes the pointer event off to the gesture system. */
private onMouseDown(e: PointerEvent) {
this.workspace.getGesture(e)?.handleBubbleStart(e, this);
this.bringToFront();
common.setSelected(this);
}

View File

@@ -4,16 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Abstract as AbstractEvent} from '../events/events_abstract.js';
import type {BlocklyOptions} from '../blockly_options.js';
import {Bubble} from './bubble.js';
import {Abstract as AbstractEvent} from '../events/events_abstract.js';
import {Options} from '../options.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Options} from '../options.js';
import {Svg} from '../utils/svg.js';
import type {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import {Bubble} from './bubble.js';
/**
* A bubble that contains a mini-workspace which can hold arbitrary blocks.

View File

@@ -4,13 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Bubble} from './bubble.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {Bubble} from './bubble.js';
/**
* A bubble that displays non-editable text. Used by the warning icon.

View File

@@ -4,16 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Bubble} from './bubble.js';
import {Coordinate} from '../utils/coordinate.js';
import * as Css from '../css.js';
import * as touch from '../touch.js';
import {browserEvents} from '../utils.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import * as touch from '../touch.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {browserEvents} from '../utils.js';
import {Bubble} from './bubble.js';
/**
* A bubble that displays editable text. It can also be resized by the user.

View File

@@ -12,7 +12,9 @@ import type {BlockCreate} from './events/events_block_create.js';
import type {BlockMove} from './events/events_block_move.js';
import type {CommentCreate} from './events/events_comment_create.js';
import type {CommentMove} from './events/events_comment_move.js';
import type {ViewportChange} from './events/events_viewport.js';
import type {CommentResize} from './events/events_comment_resize.js';
import {isViewportChange} from './events/predicates.js';
import {BUMP_EVENTS, EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
import type {ContainerRegion} from './metrics_manager.js';
@@ -98,7 +100,7 @@ export function bumpIntoBoundsHandler(
return;
}
if (eventUtils.BUMP_EVENTS.includes(e.type ?? '')) {
if (BUMP_EVENTS.includes(e.type ?? '')) {
const scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true);
// Triggered by move/create event
@@ -126,13 +128,8 @@ export function bumpIntoBoundsHandler(
);
}
eventUtils.setGroup(existingGroup);
} else if (e.type === eventUtils.VIEWPORT_CHANGE) {
const viewportEvent = e as ViewportChange;
if (
viewportEvent.scale &&
viewportEvent.oldScale &&
viewportEvent.scale > viewportEvent.oldScale
) {
} else if (isViewportChange(e)) {
if (e.scale && e.oldScale && e.scale > e.oldScale) {
bumpTopObjectsIntoBounds(workspace);
}
}
@@ -154,17 +151,18 @@ function extractObjectFromEvent(
): IBoundedElement | null {
let object = null;
switch (e.type) {
case eventUtils.BLOCK_CREATE:
case eventUtils.BLOCK_MOVE:
case EventType.BLOCK_CREATE:
case EventType.BLOCK_MOVE:
object = workspace.getBlockById((e as BlockCreate | BlockMove).blockId!);
if (object) {
object = object.getRootBlock();
}
break;
case eventUtils.COMMENT_CREATE:
case eventUtils.COMMENT_MOVE:
case EventType.COMMENT_CREATE:
case EventType.COMMENT_MOVE:
case EventType.COMMENT_RESIZE:
object = workspace.getCommentById(
(e as CommentCreate | CommentMove).commentId!,
(e as CommentCreate | CommentMove | CommentResize).commentId!,
) as RenderedWorkspaceComment;
break;
}

View File

@@ -6,12 +6,12 @@
// Former goog.module ID: Blockly.clipboard
import type {ICopyData, ICopyable} from './interfaces/i_copyable.js';
import {BlockPaster} from './clipboard/block_paster.js';
import * as globalRegistry from './registry.js';
import {WorkspaceSvg} from './workspace_svg.js';
import * as registry from './clipboard/registry.js';
import type {ICopyData, ICopyable} from './interfaces/i_copyable.js';
import * as globalRegistry from './registry.js';
import {Coordinate} from './utils/coordinate.js';
import {WorkspaceSvg} from './workspace_svg.js';
/** Metadata about the object that is currently on the clipboard. */
let stashedCopyData: ICopyData | null = null;

View File

@@ -5,15 +5,16 @@
*/
import {BlockSvg} from '../block_svg.js';
import * as registry from './registry.js';
import * as common from '../common.js';
import {config} from '../config.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import {ICopyData} from '../interfaces/i_copyable.js';
import {IPaster} from '../interfaces/i_paster.js';
import {State, append} from '../serialization/blocks.js';
import {Coordinate} from '../utils/coordinate.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import * as eventUtils from '../events/utils.js';
import {config} from '../config.js';
import * as common from '../common.js';
import * as registry from './registry.js';
export class BlockPaster implements IPaster<BlockCopyData, BlockSvg> {
static TYPE = 'block';
@@ -52,7 +53,7 @@ export class BlockPaster implements IPaster<BlockCopyData, BlockSvg> {
if (!block) return block;
if (eventUtils.isEnabled() && !block.isShadow()) {
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block));
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_CREATE))(block));
}
common.setSelected(block);
return block;

View File

@@ -4,15 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {IPaster} from '../interfaces/i_paster.js';
import {RenderedWorkspaceComment} from '../comments/rendered_workspace_comment.js';
import * as common from '../common.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import {ICopyData} from '../interfaces/i_copyable.js';
import {IPaster} from '../interfaces/i_paster.js';
import * as commentSerialiation from '../serialization/workspace_comments.js';
import {Coordinate} from '../utils/coordinate.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import * as registry from './registry.js';
import * as commentSerialiation from '../serialization/workspace_comments.js';
import * as eventUtils from '../events/utils.js';
import * as common from '../common.js';
import {RenderedWorkspaceComment} from '../comments/rendered_workspace_comment.js';
export class WorkspaceCommentPaster
implements IPaster<WorkspaceCommentCopyData, RenderedWorkspaceComment>
@@ -46,7 +47,7 @@ export class WorkspaceCommentPaster
if (!comment) return null;
if (eventUtils.isEnabled()) {
eventUtils.fire(new (eventUtils.get(eventUtils.COMMENT_CREATE))(comment));
eventUtils.fire(new (eventUtils.get(EventType.COMMENT_CREATE))(comment));
}
common.setSelected(comment);
return comment;

View File

@@ -5,5 +5,5 @@
*/
export {CommentView} from './comments/comment_view.js';
export {WorkspaceComment} from './comments/workspace_comment.js';
export {RenderedWorkspaceComment} from './comments/rendered_workspace_comment.js';
export {WorkspaceComment} from './comments/workspace_comment.js';

View File

@@ -4,16 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {IRenderedElement} from '../interfaces/i_rendered_element.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import * as dom from '../utils/dom.js';
import {Svg} from '../utils/svg.js';
import * as layers from '../layers.js';
import * as css from '../css.js';
import {Coordinate} from '../utils/coordinate.js';
import {Size} from '../utils/size.js';
import * as browserEvents from '../browser_events.js';
import * as css from '../css.js';
import {IRenderedElement} from '../interfaces/i_rendered_element.js';
import * as layers from '../layers.js';
import * as touch from '../touch.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import {WorkspaceSvg} from '../workspace_svg.js';
export class CommentView implements IRenderedElement {
/** The root group element of the comment view. */
@@ -99,9 +99,12 @@ export class CommentView implements IRenderedElement {
/** Whether this comment view has been disposed or not. */
private disposed = false;
/** Size of this comment when the resize drag was initiated. */
private preResizeSize?: Size;
constructor(private readonly workspace: WorkspaceSvg) {
this.svgRoot = dom.createSvgElement(Svg.G, {
'class': 'blocklyComment blocklyEditable',
'class': 'blocklyComment blocklyEditable blocklyDraggable',
});
this.highlightRect = this.createHighlightRect(this.svgRoot);
@@ -125,7 +128,7 @@ export class CommentView implements IRenderedElement {
workspace.getLayerManager()?.append(this, layers.BLOCK);
// Set size to the default size.
this.setSize(this.size);
this.setSizeWithoutFiringEvents(this.size);
// Set default transform (including inverted scale for RTL).
this.moveTo(new Coordinate(0, 0));
@@ -298,7 +301,7 @@ export class CommentView implements IRenderedElement {
* Sets the size of the comment in workspace units, and updates the view
* elements to reflect the new size.
*/
setSize(size: Size) {
setSizeWithoutFiringEvents(size: Size) {
const topBarSize = this.topBarBackground.getBBox();
const deleteSize = this.deleteIcon.getBBox();
const foldoutSize = this.foldoutIcon.getBBox();
@@ -309,7 +312,6 @@ export class CommentView implements IRenderedElement {
size,
this.calcMinSize(topBarSize, foldoutSize, deleteSize),
);
const oldSize = this.size;
this.size = size;
this.svgRoot.setAttribute('height', `${size.height}`);
@@ -328,7 +330,15 @@ export class CommentView implements IRenderedElement {
resizeSize,
);
this.updateResizeHandlePosition(size, resizeSize);
}
/**
* Sets the size of the comment in workspace units, updates the view
* elements to reflect the new size, and triggers size change listeners.
*/
setSize(size: Size) {
const oldSize = this.preResizeSize || this.size;
this.setSizeWithoutFiringEvents(size);
this.onSizeChange(oldSize, this.size);
}
@@ -472,7 +482,7 @@ export class CommentView implements IRenderedElement {
/**
* Triggers listeners when the size of the comment changes, either
* progrmatically or manually by the user.
* programmatically or manually by the user.
*/
private onSizeChange(oldSize: Size, newSize: Size) {
// Loop through listeners backwards in case they remove themselves.
@@ -512,6 +522,8 @@ export class CommentView implements IRenderedElement {
return;
}
this.preResizeSize = this.getSize();
// TODO(#7926): Move this into a utils file.
this.workspace.startDrag(
e,
@@ -550,13 +562,18 @@ export class CommentView implements IRenderedElement {
browserEvents.unbind(this.resizePointerMoveListener);
this.resizePointerMoveListener = null;
}
// When ending a resize drag, notify size change listeners to fire an event.
this.setSize(this.size);
this.preResizeSize = undefined;
}
/** Resizes the comment in response to a drag on the resize handle. */
private onResizePointerMove(e: PointerEvent) {
// TODO(#7926): Move this into a utils file.
const delta = this.workspace.moveDrag(e);
this.setSize(new Size(this.workspace.RTL ? -delta.x : delta.x, delta.y));
const size = this.workspace.moveDrag(e);
this.setSizeWithoutFiringEvents(
new Size(this.workspace.RTL ? -size.x : size.x, size.y),
);
}
/** Returns true if the comment is currently collapsed. */
@@ -573,7 +590,7 @@ export class CommentView implements IRenderedElement {
dom.removeClass(this.svgRoot, 'blocklyCollapsed');
}
// Repositions resize handle and such.
this.setSize(this.size);
this.setSizeWithoutFiringEvents(this.size);
this.onCollapse();
}
@@ -682,7 +699,7 @@ export class CommentView implements IRenderedElement {
/**
* Triggers listeners when the text of the comment changes, either
* progrmatically or manually by the user.
* programmatically or manually by the user.
*/
private onTextChange() {
const oldText = this.text;
@@ -810,7 +827,6 @@ css.register(`
}
.blocklyCommentTopbarBackground {
cursor: grab;
fill: var(--commentBorderColour);
height: 24px;
}

View File

@@ -4,30 +4,31 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {WorkspaceComment} from './workspace_comment.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {CommentView} from './comment_view.js';
import {Coordinate} from '../utils/coordinate.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {IBoundedElement} from '../interfaces/i_bounded_element.js';
import {IRenderedElement} from '../interfaces/i_rendered_element.js';
import * as dom from '../utils/dom.js';
import {IDraggable} from '../interfaces/i_draggable.js';
import {CommentDragStrategy} from '../dragging/comment_drag_strategy.js';
import * as browserEvents from '../browser_events.js';
import * as common from '../common.js';
import {ISelectable} from '../interfaces/i_selectable.js';
import {IDeletable} from '../interfaces/i_deletable.js';
import {ICopyable} from '../interfaces/i_copyable.js';
import * as commentSerialization from '../serialization/workspace_comments.js';
import {
WorkspaceCommentPaster,
WorkspaceCommentCopyData,
WorkspaceCommentPaster,
} from '../clipboard/workspace_comment_paster.js';
import {IContextMenu} from '../interfaces/i_contextmenu.js';
import * as common from '../common.js';
import * as contextMenu from '../contextmenu.js';
import {ContextMenuRegistry} from '../contextmenu_registry.js';
import {CommentDragStrategy} from '../dragging/comment_drag_strategy.js';
import {IBoundedElement} from '../interfaces/i_bounded_element.js';
import {IContextMenu} from '../interfaces/i_contextmenu.js';
import {ICopyable} from '../interfaces/i_copyable.js';
import {IDeletable} from '../interfaces/i_deletable.js';
import {IDraggable} from '../interfaces/i_draggable.js';
import {IRenderedElement} from '../interfaces/i_rendered_element.js';
import {ISelectable} from '../interfaces/i_selectable.js';
import * as layers from '../layers.js';
import * as commentSerialization from '../serialization/workspace_comments.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {CommentView} from './comment_view.js';
import {WorkspaceComment} from './workspace_comment.js';
export class RenderedWorkspaceComment
extends WorkspaceComment
@@ -57,6 +58,7 @@ export class RenderedWorkspaceComment
// Set the size to the default size as defined in the superclass.
this.view.setSize(this.getSize());
this.view.setEditable(this.isEditable());
this.view.getSvgRoot().setAttribute('data-id', this.id);
this.addModelUpdateBindings();
@@ -207,6 +209,7 @@ export class RenderedWorkspaceComment
const gesture = this.workspace.getGesture(e);
if (gesture) {
gesture.handleCommentStart(e, this);
this.workspace.getLayerManager()?.append(this, layers.BLOCK);
common.setSelected(this);
}
}

View File

@@ -4,12 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Workspace} from '../workspace.js';
import {Size} from '../utils/size.js';
import {CommentMove} from '../events/events_comment_move.js';
import {CommentResize} from '../events/events_comment_resize.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import {Coordinate} from '../utils/coordinate.js';
import * as idGenerator from '../utils/idgenerator.js';
import * as eventUtils from '../events/utils.js';
import {CommentMove} from '../events/events_comment_move.js';
import {Size} from '../utils/size.js';
import {Workspace} from '../workspace.js';
export class WorkspaceComment {
/** The unique identifier for this comment. */
@@ -62,13 +64,13 @@ export class WorkspaceComment {
private fireCreateEvent() {
if (eventUtils.isEnabled()) {
eventUtils.fire(new (eventUtils.get(eventUtils.COMMENT_CREATE))(this));
eventUtils.fire(new (eventUtils.get(EventType.COMMENT_CREATE))(this));
}
}
private fireDeleteEvent() {
if (eventUtils.isEnabled()) {
eventUtils.fire(new (eventUtils.get(eventUtils.COMMENT_DELETE))(this));
eventUtils.fire(new (eventUtils.get(EventType.COMMENT_DELETE))(this));
}
}
@@ -76,7 +78,7 @@ export class WorkspaceComment {
private fireChangeEvent(oldText: string, newText: string) {
if (eventUtils.isEnabled()) {
eventUtils.fire(
new (eventUtils.get(eventUtils.COMMENT_CHANGE))(this, oldText, newText),
new (eventUtils.get(EventType.COMMENT_CHANGE))(this, oldText, newText),
);
}
}
@@ -85,7 +87,7 @@ export class WorkspaceComment {
private fireCollapseEvent(newCollapsed: boolean) {
if (eventUtils.isEnabled()) {
eventUtils.fire(
new (eventUtils.get(eventUtils.COMMENT_COLLAPSE))(this, newCollapsed),
new (eventUtils.get(EventType.COMMENT_COLLAPSE))(this, newCollapsed),
);
}
}
@@ -104,7 +106,14 @@ export class WorkspaceComment {
/** Sets the comment's size in workspace units. */
setSize(size: Size) {
const event = new (eventUtils.get(EventType.COMMENT_RESIZE))(
this,
) as CommentResize;
this.size = size;
event.recordCurrentSizeAsNewSize();
eventUtils.fire(event);
}
/** Returns the comment's size in workspace units. */
@@ -175,7 +184,11 @@ export class WorkspaceComment {
* workspace is read-only.
*/
isDeletable(): boolean {
return this.isOwnDeletable() && !this.workspace.options.readOnly;
return (
this.isOwnDeletable() &&
!this.isDeadOrDying() &&
!this.workspace.options.readOnly
);
}
/**
@@ -188,7 +201,7 @@ export class WorkspaceComment {
/** Moves the comment to the given location in workspace coordinates. */
moveTo(location: Coordinate, reason?: string[] | undefined) {
const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))(
const event = new (eventUtils.get(EventType.COMMENT_MOVE))(
this,
) as CommentMove;
if (reason) event.setReason(reason);
@@ -196,7 +209,7 @@ export class WorkspaceComment {
this.location = location;
event.recordNew();
if (eventUtils.isEnabled()) eventUtils.fire(event);
eventUtils.fire(event);
}
/** Returns the position of the comment in workspace coordinates. */

View File

@@ -6,14 +6,14 @@
// Former goog.module ID: Blockly.common
/* eslint-disable-next-line no-unused-vars */
import type {Block} from './block.js';
import {ISelectable} from './blockly.js';
import {BlockDefinition, Blocks} from './blocks.js';
import type {Connection} from './connection.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import type {Workspace} from './workspace.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as eventUtils from './events/utils.js';
/** Database of all workspaces. */
const WorkspaceDB_ = Object.create(null);
@@ -108,7 +108,7 @@ export function getSelected(): ISelectable | null {
export function setSelected(newSelection: ISelectable | null) {
if (selected === newSelection) return;
const event = new (eventUtils.get(eventUtils.SELECTED))(
const event = new (eventUtils.get(EventType.SELECTED))(
selected?.id ?? null,
newSelection?.id ?? null,
newSelection?.workspace.id ?? selected?.workspace.id ?? '',

View File

@@ -224,6 +224,16 @@ export class ComponentManager {
}
export namespace ComponentManager {
export enum ComponentWeight {
// The toolbox weight is lower (higher precedence) than the flyout, so that
// if both are under the pointer, the toolbox takes precedence even though
// the flyout's drag target area is large enough to include the toolbox.
TOOLBOX_WEIGHT = 0,
FLYOUT_WEIGHT = 1,
TRASHCAN_WEIGHT = 2,
ZOOM_CONTROLS_WEIGHT = 3,
}
/** An object storing component information. */
export interface ComponentDatum {
component: IComponent;
@@ -232,4 +242,6 @@ export namespace ComponentManager {
}
}
export type ComponentWeight = ComponentManager.ComponentWeight;
export const ComponentWeight = ComponentManager.ComponentWeight;
export type ComponentDatum = ComponentManager.ComponentDatum;

View File

@@ -14,6 +14,7 @@
import type {Block} from './block.js';
import {ConnectionType} from './connection_type.js';
import type {BlockMove} from './events/events_block_move.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import type {Input} from './inputs/input.js';
import type {IASTNodeLocationWithBlock} from './interfaces/i_ast_node_location_with_block.js';
@@ -114,7 +115,7 @@ export class Connection implements IASTNodeLocationWithBlock {
// Connect the new connection to the parent.
let event;
if (eventUtils.isEnabled()) {
event = new (eventUtils.get(eventUtils.BLOCK_MOVE))(
event = new (eventUtils.get(EventType.BLOCK_MOVE))(
childBlock,
) as BlockMove;
event.setReason(['connect']);
@@ -281,7 +282,7 @@ export class Connection implements IASTNodeLocationWithBlock {
let event;
if (eventUtils.isEnabled()) {
event = new (eventUtils.get(eventUtils.BLOCK_MOVE))(
event = new (eventUtils.get(EventType.BLOCK_MOVE))(
childConnection.getSourceBlock(),
) as BlockMove;
event.setReason(['disconnect']);

View File

@@ -9,23 +9,24 @@
import type {Block} from './block.js';
import type {BlockSvg} from './block_svg.js';
import * as browserEvents from './browser_events.js';
import * as common from './common.js';
import {config} from './config.js';
import * as dom from './utils/dom.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from './contextmenu_registry.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import {Menu} from './menu.js';
import {MenuItem} from './menuitem.js';
import * as aria from './utils/aria.js';
import {Rect} from './utils/rect.js';
import * as serializationBlocks from './serialization/blocks.js';
import * as aria from './utils/aria.js';
import * as dom from './utils/dom.js';
import {Rect} from './utils/rect.js';
import * as svgMath from './utils/svg_math.js';
import * as WidgetDiv from './widgetdiv.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as Xml from './xml.js';
import * as common from './common.js';
/**
* Which block is the context menu attached to?
@@ -260,7 +261,7 @@ export function callbackFactory(
eventUtils.enable();
}
if (eventUtils.isEnabled() && !newBlock.isShadow()) {
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(newBlock));
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_CREATE))(newBlock));
}
common.setSelected(newBlock);
return newBlock;

View File

@@ -9,12 +9,13 @@
import type {BlockSvg} from './block_svg.js';
import * as clipboard from './clipboard.js';
import {RenderedWorkspaceComment} from './comments/rendered_workspace_comment.js';
import * as common from './common.js';
import {MANUALLY_DISABLED} from './constants.js';
import {
ContextMenuRegistry,
RegistryItem,
Scope,
} from './contextmenu_registry.js';
import {MANUALLY_DISABLED} from './constants.js';
import * as dialog from './dialog.js';
import * as Events from './events/events.js';
import * as eventUtils from './events/utils.js';
@@ -23,7 +24,6 @@ import {Msg} from './msg.js';
import {StatementInput} from './renderers/zelos/zelos.js';
import {Coordinate} from './utils/coordinate.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as common from './common.js';
/**
* Option to undo previous action.

View File

@@ -14,9 +14,9 @@
import {BlockSvg} from './block_svg.js';
import {DragTarget} from './drag_target.js';
import {isDeletable} from './interfaces/i_deletable.js';
import type {IDeleteArea} from './interfaces/i_delete_area.js';
import type {IDraggable} from './interfaces/i_draggable.js';
import {isDeletable} from './interfaces/i_deletable.js';
/**
* Abstract class for a component that can delete a block or bubble that is

View File

@@ -4,9 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Dragger} from './dragging/dragger.js';
import {BlockDragStrategy} from './dragging/block_drag_strategy.js';
import {BubbleDragStrategy} from './dragging/bubble_drag_strategy.js';
import {CommentDragStrategy} from './dragging/comment_drag_strategy.js';
import {Dragger} from './dragging/dragger.js';
export {Dragger, BlockDragStrategy, BubbleDragStrategy, CommentDragStrategy};
export {BlockDragStrategy, BubbleDragStrategy, CommentDragStrategy, Dragger};

View File

@@ -4,24 +4,25 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {WorkspaceSvg} from '../workspace_svg.js';
import {IDragStrategy} from '../interfaces/i_draggable.js';
import {Coordinate} from '../utils.js';
import * as eventUtils from '../events/utils.js';
import {BlockSvg} from '../block_svg.js';
import {RenderedConnection} from '../rendered_connection.js';
import * as dom from '../utils/dom.js';
import * as blockAnimation from '../block_animations.js';
import {ConnectionType} from '../connection_type.js';
import * as bumpObjects from '../bump_objects.js';
import * as registry from '../registry.js';
import {IConnectionPreviewer} from '../interfaces/i_connection_previewer.js';
import {Connection} from '../connection.js';
import type {Block} from '../block.js';
import * as blockAnimation from '../block_animations.js';
import {BlockSvg} from '../block_svg.js';
import * as bumpObjects from '../bump_objects.js';
import {config} from '../config.js';
import {Connection} from '../connection.js';
import {ConnectionType} from '../connection_type.js';
import type {BlockMove} from '../events/events_block_move.js';
import {finishQueuedRenders} from '../render_management.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import {IConnectionPreviewer} from '../interfaces/i_connection_previewer.js';
import {IDragStrategy} from '../interfaces/i_draggable.js';
import * as layers from '../layers.js';
import * as registry from '../registry.js';
import {finishQueuedRenders} from '../render_management.js';
import {RenderedConnection} from '../rendered_connection.js';
import {Coordinate} from '../utils.js';
import * as dom from '../utils/dom.js';
import {WorkspaceSvg} from '../workspace_svg.js';
/** Represents a nearby valid connection. */
interface ConnectionCandidate {
@@ -61,6 +62,9 @@ export class BlockDragStrategy implements IDragStrategy {
*/
private dragOffset = new Coordinate(0, 0);
/** Was there already an event group in progress when the drag started? */
private inGroup: boolean = false;
constructor(private block: BlockSvg) {
this.workspace = block.workspace;
}
@@ -92,7 +96,8 @@ export class BlockDragStrategy implements IDragStrategy {
}
this.dragging = true;
if (!eventUtils.getGroup()) {
this.inGroup = !!eventUtils.getGroup();
if (!this.inGroup) {
eventUtils.setGroup(true);
}
this.fireDragStartEvent();
@@ -173,7 +178,7 @@ export class BlockDragStrategy implements IDragStrategy {
/** Fire a UI event at the start of a block drag. */
private fireDragStartEvent() {
const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))(
const event = new (eventUtils.get(EventType.BLOCK_DRAG))(
this.block,
true,
this.block.getDescendants(false),
@@ -183,7 +188,7 @@ export class BlockDragStrategy implements IDragStrategy {
/** Fire a UI event at the end of a block drag. */
private fireDragEndEvent() {
const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))(
const event = new (eventUtils.get(EventType.BLOCK_DRAG))(
this.block,
false,
this.block.getDescendants(false),
@@ -194,7 +199,7 @@ export class BlockDragStrategy implements IDragStrategy {
/** Fire a move event at the end of a block drag. */
private fireMoveEvent() {
if (this.block.isDeadOrDying()) return;
const event = new (eventUtils.get(eventUtils.BLOCK_MOVE))(
const event = new (eventUtils.get(EventType.BLOCK_MOVE))(
this.block,
) as BlockMove;
event.setReason(['drag']);
@@ -389,7 +394,9 @@ export class BlockDragStrategy implements IDragStrategy {
this.connectionPreviewer!.dispose();
this.workspace.setResizesEnabled(true);
eventUtils.setGroup(false);
if (!this.inGroup) {
eventUtils.setGroup(false);
}
}
/** Connects the given candidate connections. */

View File

@@ -4,15 +4,18 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {IDragStrategy} from '../interfaces/i_draggable.js';
import {Coordinate} from '../utils.js';
import * as eventUtils from '../events/utils.js';
import {IBubble, WorkspaceSvg} from '../blockly.js';
import * as eventUtils from '../events/utils.js';
import {IDragStrategy} from '../interfaces/i_draggable.js';
import * as layers from '../layers.js';
import {Coordinate} from '../utils.js';
export class BubbleDragStrategy implements IDragStrategy {
private startLoc: Coordinate | null = null;
/** Was there already an event group in progress when the drag started? */
private inGroup: boolean = false;
constructor(
private bubble: IBubble,
private workspace: WorkspaceSvg,
@@ -23,13 +26,16 @@ export class BubbleDragStrategy implements IDragStrategy {
}
startDrag(): void {
if (!eventUtils.getGroup()) {
this.inGroup = !!eventUtils.getGroup();
if (!this.inGroup) {
eventUtils.setGroup(true);
}
this.startLoc = this.bubble.getRelativeToSurfaceXY();
this.workspace.setResizesEnabled(false);
this.workspace.getLayerManager()?.moveToDragLayer(this.bubble);
this.bubble.setDragging && this.bubble.setDragging(true);
if (this.bubble.setDragging) {
this.bubble.setDragging(true);
}
}
drag(newLoc: Coordinate): void {
@@ -38,7 +44,9 @@ export class BubbleDragStrategy implements IDragStrategy {
endDrag(): void {
this.workspace.setResizesEnabled(true);
eventUtils.setGroup(false);
if (!this.inGroup) {
eventUtils.setGroup(false);
}
this.workspace
.getLayerManager()

View File

@@ -4,31 +4,41 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {IDragStrategy} from '../interfaces/i_draggable.js';
import {Coordinate} from '../utils.js';
import * as eventUtils from '../events/utils.js';
import * as layers from '../layers.js';
import {RenderedWorkspaceComment} from '../comments.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {CommentMove} from '../events/events_comment_move.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import {IDragStrategy} from '../interfaces/i_draggable.js';
import * as layers from '../layers.js';
import {Coordinate} from '../utils.js';
import {WorkspaceSvg} from '../workspace_svg.js';
export class CommentDragStrategy implements IDragStrategy {
private startLoc: Coordinate | null = null;
private workspace: WorkspaceSvg;
/** Was there already an event group in progress when the drag started? */
private inGroup: boolean = false;
constructor(private comment: RenderedWorkspaceComment) {
this.workspace = comment.workspace;
}
isMovable(): boolean {
return this.comment.isOwnMovable() && !this.workspace.options.readOnly;
return (
this.comment.isOwnMovable() &&
!this.comment.isDeadOrDying() &&
!this.workspace.options.readOnly
);
}
startDrag(): void {
if (!eventUtils.getGroup()) {
this.inGroup = !!eventUtils.getGroup();
if (!this.inGroup) {
eventUtils.setGroup(true);
}
this.fireDragStartEvent();
this.startLoc = this.comment.getRelativeToSurfaceXY();
this.workspace.setResizesEnabled(false);
this.workspace.getLayerManager()?.moveToDragLayer(this.comment);
@@ -40,6 +50,7 @@ export class CommentDragStrategy implements IDragStrategy {
}
endDrag(): void {
this.fireDragEndEvent();
this.fireMoveEvent();
this.workspace
@@ -50,12 +61,33 @@ export class CommentDragStrategy implements IDragStrategy {
this.comment.snapToGrid();
this.workspace.setResizesEnabled(true);
eventUtils.setGroup(false);
if (!this.inGroup) {
eventUtils.setGroup(false);
}
}
/** Fire a UI event at the start of a comment drag. */
private fireDragStartEvent() {
const event = new (eventUtils.get(EventType.COMMENT_DRAG))(
this.comment,
true,
);
eventUtils.fire(event);
}
/** Fire a UI event at the end of a comment drag. */
private fireDragEndEvent() {
const event = new (eventUtils.get(EventType.COMMENT_DRAG))(
this.comment,
false,
);
eventUtils.fire(event);
}
/** Fire a move event at the end of a comment drag. */
private fireMoveEvent() {
if (this.comment.isDeadOrDying()) return;
const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))(
const event = new (eventUtils.get(EventType.COMMENT_MOVE))(
this.comment,
) as CommentMove;
event.setReason(['drag']);

View File

@@ -4,18 +4,18 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {IDragTarget} from '../interfaces/i_drag_target.js';
import {IDeletable, isDeletable} from '../interfaces/i_deletable.js';
import {IDragger} from '../interfaces/i_dragger.js';
import {IDraggable} from '../interfaces/i_draggable.js';
import {Coordinate} from '../utils/coordinate.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {ComponentManager} from '../component_manager.js';
import {IDeleteArea} from '../interfaces/i_delete_area.js';
import * as registry from '../registry.js';
import * as eventUtils from '../events/utils.js';
import * as blockAnimations from '../block_animations.js';
import {BlockSvg} from '../block_svg.js';
import {ComponentManager} from '../component_manager.js';
import * as eventUtils from '../events/utils.js';
import {IDeletable, isDeletable} from '../interfaces/i_deletable.js';
import {IDeleteArea} from '../interfaces/i_delete_area.js';
import {IDragTarget} from '../interfaces/i_drag_target.js';
import {IDraggable} from '../interfaces/i_draggable.js';
import {IDragger} from '../interfaces/i_dragger.js';
import * as registry from '../registry.js';
import {Coordinate} from '../utils/coordinate.js';
import {WorkspaceSvg} from '../workspace_svg.js';
export class Dragger implements IDragger {
protected startLoc: Coordinate;

View File

@@ -14,8 +14,8 @@
import type {BlockSvg} from './block_svg.js';
import * as common from './common.js';
import * as dom from './utils/dom.js';
import type {Field} from './field.js';
import * as dom from './utils/dom.js';
import * as math from './utils/math.js';
import {Rect} from './utils/rect.js';
import type {Size} from './utils/size.js';
@@ -53,7 +53,7 @@ export const ANIMATION_TIME = 0.25;
let animateOutTimer: ReturnType<typeof setTimeout> | null = null;
/** Callback for when the drop-down is hidden. */
let onHide: Function | null = null;
let onHide: (() => void) | null = null;
/** A class name representing the current owner's workspace renderer. */
let renderedClassName = '';
@@ -202,7 +202,7 @@ export function setColour(backgroundColour: string, borderColour: string) {
export function showPositionedByBlock<T>(
field: Field<T>,
block: BlockSvg,
opt_onHide?: Function,
opt_onHide?: () => void,
opt_secondaryYOffset?: number,
): boolean {
return showPositionedByRect(
@@ -226,7 +226,7 @@ export function showPositionedByBlock<T>(
*/
export function showPositionedByField<T>(
field: Field<T>,
opt_onHide?: Function,
opt_onHide?: () => void,
opt_secondaryYOffset?: number,
): boolean {
positionToField = true;
@@ -278,7 +278,7 @@ function getScaledBboxOfField(field: Field): Rect {
function showPositionedByRect(
bBox: Rect,
field: Field,
opt_onHide?: Function,
opt_onHide?: () => void,
opt_secondaryYOffset?: number,
): boolean {
// If we can fit it, render below the block.
@@ -334,7 +334,7 @@ export function show<T>(
primaryY: number,
secondaryX: number,
secondaryY: number,
opt_onHide?: Function,
opt_onHide?: () => void,
): boolean {
owner = newOwner as Field;
onHide = opt_onHide || null;

View File

@@ -6,146 +6,103 @@
// Former goog.module ID: Blockly.Events
import {Abstract, AbstractEventJson} from './events_abstract.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import {BlockChange, BlockChangeJson} from './events_block_change.js';
import {BlockCreate, BlockCreateJson} from './events_block_create.js';
import {BlockDelete, BlockDeleteJson} from './events_block_delete.js';
import {BlockDrag, BlockDragJson} from './events_block_drag.js';
import {
import {EventType} from './type.js';
// Events.
export {Abstract, AbstractEventJson} from './events_abstract.js';
export {BlockBase, BlockBaseJson} from './events_block_base.js';
export {BlockChange, BlockChangeJson} from './events_block_change.js';
export {BlockCreate, BlockCreateJson} from './events_block_create.js';
export {BlockDelete, BlockDeleteJson} from './events_block_delete.js';
export {BlockDrag, BlockDragJson} from './events_block_drag.js';
export {
BlockFieldIntermediateChange,
BlockFieldIntermediateChangeJson,
} from './events_block_field_intermediate_change.js';
import {BlockMove, BlockMoveJson} from './events_block_move.js';
import {BubbleOpen, BubbleOpenJson, BubbleType} from './events_bubble_open.js';
import {Click, ClickJson, ClickTarget} from './events_click.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import {CommentChange, CommentChangeJson} from './events_comment_change.js';
import {CommentCreate, CommentCreateJson} from './events_comment_create.js';
import {CommentDelete} from './events_comment_delete.js';
import {CommentMove, CommentMoveJson} from './events_comment_move.js';
import {
export {BlockMove, BlockMoveJson} from './events_block_move.js';
export {BubbleOpen, BubbleOpenJson, BubbleType} from './events_bubble_open.js';
export {Click, ClickJson, ClickTarget} from './events_click.js';
export {CommentBase, CommentBaseJson} from './events_comment_base.js';
export {CommentChange, CommentChangeJson} from './events_comment_change.js';
export {
CommentCollapse,
CommentCollapseJson,
} from './events_comment_collapse.js';
import {MarkerMove, MarkerMoveJson} from './events_marker_move.js';
import {Selected, SelectedJson} from './events_selected.js';
import {ThemeChange, ThemeChangeJson} from './events_theme_change.js';
import {
export {CommentCreate, CommentCreateJson} from './events_comment_create.js';
export {CommentDelete} from './events_comment_delete.js';
export {CommentDrag, CommentDragJson} from './events_comment_drag.js';
export {CommentMove, CommentMoveJson} from './events_comment_move.js';
export {CommentResize, CommentResizeJson} from './events_comment_resize.js';
export {MarkerMove, MarkerMoveJson} from './events_marker_move.js';
export {Selected, SelectedJson} from './events_selected.js';
export {ThemeChange, ThemeChangeJson} from './events_theme_change.js';
export {
ToolboxItemSelect,
ToolboxItemSelectJson,
} from './events_toolbox_item_select.js';
import {TrashcanOpen, TrashcanOpenJson} from './events_trashcan_open.js';
import {UiBase} from './events_ui_base.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import {VarCreate, VarCreateJson} from './events_var_create.js';
import {VarDelete, VarDeleteJson} from './events_var_delete.js';
import {VarRename, VarRenameJson} from './events_var_rename.js';
import {ViewportChange, ViewportChangeJson} from './events_viewport.js';
import * as eventUtils from './utils.js';
import {FinishedLoading} from './workspace_events.js';
export {TrashcanOpen, TrashcanOpenJson} from './events_trashcan_open.js';
export {UiBase} from './events_ui_base.js';
export {VarBase, VarBaseJson} from './events_var_base.js';
export {VarCreate, VarCreateJson} from './events_var_create.js';
export {VarDelete, VarDeleteJson} from './events_var_delete.js';
export {VarRename, VarRenameJson} from './events_var_rename.js';
export {ViewportChange, ViewportChangeJson} from './events_viewport.js';
export {FinishedLoading} from './workspace_events.js';
// Events.
export {Abstract};
export {AbstractEventJson};
export {BubbleOpen};
export {BubbleOpenJson};
export {BubbleType};
export {BlockBase};
export {BlockBaseJson};
export {BlockChange};
export {BlockChangeJson};
export {BlockCreate};
export {BlockCreateJson};
export {BlockDelete};
export {BlockDeleteJson};
export {BlockDrag};
export {BlockDragJson};
export {BlockFieldIntermediateChange};
export {BlockFieldIntermediateChangeJson};
export {BlockMove};
export {BlockMoveJson};
export {Click};
export {ClickJson};
export {ClickTarget};
export {CommentBase};
export {CommentBaseJson};
export {CommentChange};
export {CommentChangeJson};
export {CommentCreate};
export {CommentCreateJson};
export {CommentDelete};
export {CommentMove};
export {CommentMoveJson};
export {CommentCollapse};
export {CommentCollapseJson};
export {FinishedLoading};
export {MarkerMove};
export {MarkerMoveJson};
export {Selected};
export {SelectedJson};
export {ThemeChange};
export {ThemeChangeJson};
export {ToolboxItemSelect};
export {ToolboxItemSelectJson};
export {TrashcanOpen};
export {TrashcanOpenJson};
export {UiBase};
export {VarBase};
export {VarBaseJson};
export {VarCreate};
export {VarCreateJson};
export {VarDelete};
export {VarDeleteJson};
export {VarRename};
export {VarRenameJson};
export {ViewportChange};
export {ViewportChangeJson};
export type {BumpEvent} from './utils.js';
// Event types.
export const BLOCK_CHANGE = eventUtils.BLOCK_CHANGE;
export const BLOCK_CREATE = eventUtils.BLOCK_CREATE;
export const BLOCK_DELETE = eventUtils.BLOCK_DELETE;
export const BLOCK_DRAG = eventUtils.BLOCK_DRAG;
export const BLOCK_MOVE = eventUtils.BLOCK_MOVE;
export const BLOCK_CHANGE = EventType.BLOCK_CHANGE;
export const BLOCK_CREATE = EventType.BLOCK_CREATE;
export const BLOCK_DELETE = EventType.BLOCK_DELETE;
export const BLOCK_DRAG = EventType.BLOCK_DRAG;
export const BLOCK_MOVE = EventType.BLOCK_MOVE;
export const BLOCK_FIELD_INTERMEDIATE_CHANGE =
eventUtils.BLOCK_FIELD_INTERMEDIATE_CHANGE;
export const BUBBLE_OPEN = eventUtils.BUBBLE_OPEN;
export type BumpEvent = eventUtils.BumpEvent;
export const BUMP_EVENTS = eventUtils.BUMP_EVENTS;
export const CHANGE = eventUtils.CHANGE;
export const CLICK = eventUtils.CLICK;
export const COMMENT_CHANGE = eventUtils.COMMENT_CHANGE;
export const COMMENT_CREATE = eventUtils.COMMENT_CREATE;
export const COMMENT_DELETE = eventUtils.COMMENT_DELETE;
export const COMMENT_MOVE = eventUtils.COMMENT_MOVE;
export const CREATE = eventUtils.CREATE;
export const DELETE = eventUtils.DELETE;
export const FINISHED_LOADING = eventUtils.FINISHED_LOADING;
export const MARKER_MOVE = eventUtils.MARKER_MOVE;
export const MOVE = eventUtils.MOVE;
export const SELECTED = eventUtils.SELECTED;
export const THEME_CHANGE = eventUtils.THEME_CHANGE;
export const TOOLBOX_ITEM_SELECT = eventUtils.TOOLBOX_ITEM_SELECT;
export const TRASHCAN_OPEN = eventUtils.TRASHCAN_OPEN;
export const UI = eventUtils.UI;
export const VAR_CREATE = eventUtils.VAR_CREATE;
export const VAR_DELETE = eventUtils.VAR_DELETE;
export const VAR_RENAME = eventUtils.VAR_RENAME;
export const VIEWPORT_CHANGE = eventUtils.VIEWPORT_CHANGE;
EventType.BLOCK_FIELD_INTERMEDIATE_CHANGE;
export const BUBBLE_OPEN = EventType.BUBBLE_OPEN;
/** @deprecated Use BLOCK_CHANGE instead */
export const CHANGE = EventType.BLOCK_CHANGE;
export const CLICK = EventType.CLICK;
export const COMMENT_CHANGE = EventType.COMMENT_CHANGE;
export const COMMENT_CREATE = EventType.COMMENT_CREATE;
export const COMMENT_DELETE = EventType.COMMENT_DELETE;
export const COMMENT_MOVE = EventType.COMMENT_MOVE;
export const COMMENT_RESIZE = EventType.COMMENT_RESIZE;
export const COMMENT_DRAG = EventType.COMMENT_DRAG;
/** @deprecated Use BLOCK_CREATE instead */
export const CREATE = EventType.BLOCK_CREATE;
/** @deprecated Use BLOCK_DELETE instead */
export const DELETE = EventType.BLOCK_DELETE;
export const FINISHED_LOADING = EventType.FINISHED_LOADING;
export const MARKER_MOVE = EventType.MARKER_MOVE;
/** @deprecated Use BLOCK_MOVE instead */
export const MOVE = EventType.BLOCK_MOVE;
export const SELECTED = EventType.SELECTED;
export const THEME_CHANGE = EventType.THEME_CHANGE;
export const TOOLBOX_ITEM_SELECT = EventType.TOOLBOX_ITEM_SELECT;
export const TRASHCAN_OPEN = EventType.TRASHCAN_OPEN;
export const UI = EventType.UI;
export const VAR_CREATE = EventType.VAR_CREATE;
export const VAR_DELETE = EventType.VAR_DELETE;
export const VAR_RENAME = EventType.VAR_RENAME;
export const VIEWPORT_CHANGE = EventType.VIEWPORT_CHANGE;
export {BUMP_EVENTS} from './type.js';
// Event utils.
export const clearPendingUndo = eventUtils.clearPendingUndo;
export const disable = eventUtils.disable;
export const enable = eventUtils.enable;
export const filter = eventUtils.filter;
export const fire = eventUtils.fire;
export const fromJson = eventUtils.fromJson;
export const getDescendantIds = eventUtils.getDescendantIds;
export const get = eventUtils.get;
export const getGroup = eventUtils.getGroup;
export const getRecordUndo = eventUtils.getRecordUndo;
export const isEnabled = eventUtils.isEnabled;
export const setGroup = eventUtils.setGroup;
export const setRecordUndo = eventUtils.setRecordUndo;
export const disableOrphans = eventUtils.disableOrphans;
export {
clearPendingUndo,
disable,
disableOrphans,
enable,
filter,
fire,
fromJson,
get,
getDescendantIds,
getGroup,
getRecordUndo,
isEnabled,
setGroup,
setRecordUndo,
} from './utils.js';

View File

@@ -14,8 +14,7 @@
import * as common from '../common.js';
import type {Workspace} from '../workspace.js';
import * as eventUtils from './utils.js';
import {getGroup, getRecordUndo} from './utils.js';
/**
* Abstract class for an event.
@@ -48,8 +47,8 @@ export abstract class Abstract {
type = '';
constructor() {
this.group = eventUtils.getGroup();
this.recordUndo = eventUtils.getRecordUndo();
this.group = getGroup();
this.recordUndo = getRecordUndo();
}
/**
@@ -108,7 +107,6 @@ export abstract class Abstract {
*
* @returns The workspace the event belongs to.
* @throws {Error} if workspace is null.
* @internal
*/
getEventWorkspace_(): Workspace {
let workspace;

View File

@@ -13,7 +13,6 @@
import type {Block} from '../block.js';
import type {Workspace} from '../workspace.js';
import {
Abstract as AbstractEvent,
AbstractEventJson,

View File

@@ -13,15 +13,15 @@
import type {Block} from '../block.js';
import type {BlockSvg} from '../block_svg.js';
import {MANUALLY_DISABLED} from '../constants.js';
import {IconType} from '../icons/icon_types.js';
import {hasBubble} from '../interfaces/i_has_bubble.js';
import {MANUALLY_DISABLED} from '../constants.js';
import * as registry from '../registry.js';
import * as utilsXml from '../utils/xml.js';
import {Workspace} from '../workspace.js';
import * as Xml from '../xml.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import {EventType} from './type.js';
import * as eventUtils from './utils.js';
/**
@@ -29,7 +29,7 @@ import * as eventUtils from './utils.js';
* field values, comments, etc).
*/
export class BlockChange extends BlockBase {
override type = eventUtils.BLOCK_CHANGE;
override type = EventType.BLOCK_CHANGE;
/**
* The element that changed; one of 'field', 'comment', 'collapsed',
* 'disabled', 'inline', or 'mutation'
@@ -256,4 +256,4 @@ export interface BlockChangeJson extends BlockBaseJson {
disabledReason?: string;
}
registry.register(registry.Type.EVENT, eventUtils.CHANGE, BlockChange);
registry.register(registry.Type.EVENT, EventType.BLOCK_CHANGE, BlockChange);

View File

@@ -15,18 +15,18 @@ import type {Block} from '../block.js';
import * as registry from '../registry.js';
import * as blocks from '../serialization/blocks.js';
import * as utilsXml from '../utils/xml.js';
import * as Xml from '../xml.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import * as eventUtils from './utils.js';
import {Workspace} from '../workspace.js';
import * as Xml from '../xml.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import {EventType} from './type.js';
import * as eventUtils from './utils.js';
/**
* Notifies listeners when a block (or connected stack of blocks) is
* created.
*/
export class BlockCreate extends BlockBase {
override type = eventUtils.BLOCK_CREATE;
override type = EventType.BLOCK_CREATE;
/** The XML representation of the created block(s). */
xml?: Element | DocumentFragment;
@@ -182,4 +182,4 @@ export interface BlockCreateJson extends BlockBaseJson {
recordUndo?: boolean;
}
registry.register(registry.Type.EVENT, eventUtils.CREATE, BlockCreate);
registry.register(registry.Type.EVENT, EventType.BLOCK_CREATE, BlockCreate);

View File

@@ -15,11 +15,11 @@ import type {Block} from '../block.js';
import * as registry from '../registry.js';
import * as blocks from '../serialization/blocks.js';
import * as utilsXml from '../utils/xml.js';
import * as Xml from '../xml.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import * as eventUtils from './utils.js';
import {Workspace} from '../workspace.js';
import * as Xml from '../xml.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import {EventType} from './type.js';
import * as eventUtils from './utils.js';
/**
* Notifies listeners when a block (or connected stack of blocks) is
@@ -38,7 +38,7 @@ export class BlockDelete extends BlockBase {
/** True if the deleted block was a shadow block, false otherwise. */
wasShadow?: boolean;
override type = eventUtils.BLOCK_DELETE;
override type = EventType.BLOCK_DELETE;
/** @param opt_block The deleted block. Undefined for a blank event. */
constructor(opt_block?: Block) {
@@ -179,4 +179,4 @@ export interface BlockDeleteJson extends BlockBaseJson {
recordUndo?: boolean;
}
registry.register(registry.Type.EVENT, eventUtils.DELETE, BlockDelete);
registry.register(registry.Type.EVENT, EventType.BLOCK_DELETE, BlockDelete);

View File

@@ -13,10 +13,10 @@
import type {Block} from '../block.js';
import * as registry from '../registry.js';
import {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import {Workspace} from '../workspace.js';
import {EventType} from './type.js';
/**
* Notifies listeners when a block is being manually dragged/dropped.
@@ -34,7 +34,7 @@ export class BlockDrag extends UiBase {
*/
blocks?: Block[];
override type = eventUtils.BLOCK_DRAG;
override type = EventType.BLOCK_DRAG;
/**
* @param opt_block The top block in the stack that is being dragged.
@@ -113,4 +113,4 @@ export interface BlockDragJson extends AbstractEventJson {
blocks?: Block[];
}
registry.register(registry.Type.EVENT, eventUtils.BLOCK_DRAG, BlockDrag);
registry.register(registry.Type.EVENT, EventType.BLOCK_DRAG, BlockDrag);

View File

@@ -15,9 +15,8 @@
import type {Block} from '../block.js';
import * as registry from '../registry.js';
import {Workspace} from '../workspace.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import * as eventUtils from './utils.js';
import {EventType} from './type.js';
/**
* Notifies listeners when the value of a block's field has changed but the
@@ -25,7 +24,7 @@ import * as eventUtils from './utils.js';
* event.
*/
export class BlockFieldIntermediateChange extends BlockBase {
override type = eventUtils.BLOCK_FIELD_INTERMEDIATE_CHANGE;
override type = EventType.BLOCK_FIELD_INTERMEDIATE_CHANGE;
// Intermediate events do not undo or redo. They may be fired frequently while
// the field editor widget is open. A separate BLOCK_CHANGE event is fired
@@ -162,6 +161,6 @@ export interface BlockFieldIntermediateChangeJson extends BlockBaseJson {
registry.register(
registry.Type.EVENT,
eventUtils.BLOCK_FIELD_INTERMEDIATE_CHANGE,
EventType.BLOCK_FIELD_INTERMEDIATE_CHANGE,
BlockFieldIntermediateChange,
);

View File

@@ -15,10 +15,9 @@ import type {Block} from '../block.js';
import {ConnectionType} from '../connection_type.js';
import * as registry from '../registry.js';
import {Coordinate} from '../utils/coordinate.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {BlockBase, BlockBaseJson} from './events_block_base.js';
import {EventType} from './type.js';
interface BlockLocation {
parentId?: string;
@@ -31,7 +30,7 @@ interface BlockLocation {
* connection to another, or from one location on the workspace to another.
*/
export class BlockMove extends BlockBase {
override type = eventUtils.BLOCK_MOVE;
override type = EventType.BLOCK_MOVE;
/** The ID of the old parent block. Undefined if it was a top-level block. */
oldParentId?: string;
@@ -304,4 +303,4 @@ export interface BlockMoveJson extends BlockBaseJson {
recordUndo?: boolean;
}
registry.register(registry.Type.EVENT, eventUtils.MOVE, BlockMove);
registry.register(registry.Type.EVENT, EventType.BLOCK_MOVE, BlockMove);

View File

@@ -9,14 +9,15 @@
*
* @class
*/
// Former goog.module ID: Blockly.Events.BubbleOpen
import type {AbstractEventJson} from './events_abstract.js';
import type {BlockSvg} from '../block_svg.js';
import * as registry from '../registry.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import type {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import {EventType} from './type.js';
/**
* Class for a bubble open event.
@@ -31,7 +32,7 @@ export class BubbleOpen extends UiBase {
/** The type of bubble; one of 'mutator', 'comment', or 'warning'. */
bubbleType?: BubbleType;
override type = eventUtils.BUBBLE_OPEN;
override type = EventType.BUBBLE_OPEN;
/**
* @param opt_block The associated block. Undefined for a blank event.
@@ -117,4 +118,4 @@ export interface BubbleOpenJson extends AbstractEventJson {
blockId: string;
}
registry.register(registry.Type.EVENT, eventUtils.BUBBLE_OPEN, BubbleOpen);
registry.register(registry.Type.EVENT, EventType.BUBBLE_OPEN, BubbleOpen);

View File

@@ -9,15 +9,15 @@
*
* @class
*/
// Former goog.module ID: Blockly.Events.Click
import type {Block} from '../block.js';
import * as registry from '../registry.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that some blockly element was clicked.
@@ -31,7 +31,7 @@ export class Click extends UiBase {
* or 'zoom_controls'.
*/
targetType?: ClickTarget;
override type = eventUtils.CLICK;
override type = EventType.CLICK;
/**
* @param opt_block The affected block. Null for click events that do not have
@@ -107,4 +107,4 @@ export interface ClickJson extends AbstractEventJson {
blockId?: string;
}
registry.register(registry.Type.EVENT, eventUtils.CLICK, Click);
registry.register(registry.Type.EVENT, EventType.CLICK, Click);

View File

@@ -13,14 +13,14 @@
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import * as comments from '../serialization/workspace_comments.js';
import type {Workspace} from '../workspace.js';
import {
Abstract as AbstractEvent,
AbstractEventJson,
} from './events_abstract.js';
import type {CommentCreate} from './events_comment_create.js';
import type {CommentDelete} from './events_comment_delete.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {getGroup, getRecordUndo} from './utils.js';
/**
* Abstract class for a comment event.
@@ -44,8 +44,8 @@ export class CommentBase extends AbstractEvent {
this.commentId = opt_comment.id;
this.workspaceId = opt_comment.workspace.id;
this.group = eventUtils.getGroup();
this.recordUndo = eventUtils.getRecordUndo();
this.group = getGroup();
this.recordUndo = getRecordUndo();
}
/**

View File

@@ -11,18 +11,17 @@
*/
// Former goog.module ID: Blockly.Events.CommentChange
import * as registry from '../registry.js';
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import * as eventUtils from './utils.js';
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that the contents of a workspace comment has changed.
*/
export class CommentChange extends CommentBase {
override type = eventUtils.COMMENT_CHANGE;
override type = EventType.COMMENT_CHANGE;
// TODO(#6774): We should remove underscores.
/** The previous contents of the comment. */
@@ -154,8 +153,4 @@ export interface CommentChangeJson extends CommentBaseJson {
newContents: string;
}
registry.register(
registry.Type.EVENT,
eventUtils.COMMENT_CHANGE,
CommentChange,
);
registry.register(registry.Type.EVENT, EventType.COMMENT_CHANGE, CommentChange);

View File

@@ -4,14 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import * as registry from '../registry.js';
import {WorkspaceComment} from '../comments/workspace_comment.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import * as eventUtils from './utils.js';
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import {EventType} from './type.js';
export class CommentCollapse extends CommentBase {
override type = eventUtils.COMMENT_COLLAPSE;
override type = EventType.COMMENT_COLLAPSE;
constructor(
comment?: WorkspaceComment,
@@ -98,6 +98,6 @@ export interface CommentCollapseJson extends CommentBaseJson {
registry.register(
registry.Type.EVENT,
eventUtils.COMMENT_COLLAPSE,
EventType.COMMENT_COLLAPSE,
CommentCollapse,
);

View File

@@ -11,20 +11,20 @@
*/
// Former goog.module ID: Blockly.Events.CommentCreate
import * as registry from '../registry.js';
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import * as registry from '../registry.js';
import * as comments from '../serialization/workspace_comments.js';
import * as utilsXml from '../utils/xml.js';
import type {Workspace} from '../workspace.js';
import * as Xml from '../xml.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a workspace comment was created.
*/
export class CommentCreate extends CommentBase {
override type = eventUtils.COMMENT_CREATE;
override type = EventType.COMMENT_CREATE;
/** The XML representation of the created workspace comment. */
xml?: Element | DocumentFragment;
@@ -111,8 +111,4 @@ export interface CommentCreateJson extends CommentBaseJson {
json: object;
}
registry.register(
registry.Type.EVENT,
eventUtils.COMMENT_CREATE,
CommentCreate,
);
registry.register(registry.Type.EVENT, EventType.COMMENT_CREATE, CommentCreate);

View File

@@ -11,20 +11,20 @@
*/
// Former goog.module ID: Blockly.Events.CommentDelete
import * as registry from '../registry.js';
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import * as registry from '../registry.js';
import * as comments from '../serialization/workspace_comments.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import * as eventUtils from './utils.js';
import * as utilsXml from '../utils/xml.js';
import * as Xml from '../xml.js';
import type {Workspace} from '../workspace.js';
import * as Xml from '../xml.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a workspace comment has been deleted.
*/
export class CommentDelete extends CommentBase {
override type = eventUtils.COMMENT_DELETE;
override type = EventType.COMMENT_DELETE;
/** The XML representation of the deleted workspace comment. */
xml?: Element;
@@ -110,8 +110,4 @@ export interface CommentDeleteJson extends CommentBaseJson {
json: object;
}
registry.register(
registry.Type.EVENT,
eventUtils.COMMENT_DELETE,
CommentDelete,
);
registry.register(registry.Type.EVENT, EventType.COMMENT_DELETE, CommentDelete);

View File

@@ -0,0 +1,99 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Events fired when a workspace comment is dragged.
*/
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import * as registry from '../registry.js';
import {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners when a comment is being manually dragged/dropped.
*/
export class CommentDrag extends UiBase {
/** The ID of the top-level comment being dragged. */
commentId?: string;
/** True if this is the start of a drag, false if this is the end of one. */
isStart?: boolean;
override type = EventType.COMMENT_DRAG;
/**
* @param opt_comment The comment that is being dragged.
* Undefined for a blank event.
* @param opt_isStart Whether this is the start of a comment drag.
* Undefined for a blank event.
*/
constructor(opt_comment?: WorkspaceComment, opt_isStart?: boolean) {
const workspaceId = opt_comment ? opt_comment.workspace.id : undefined;
super(workspaceId);
if (!opt_comment) return;
this.commentId = opt_comment.id;
this.isStart = opt_isStart;
}
/**
* Encode the event as JSON.
*
* @returns JSON representation.
*/
override toJson(): CommentDragJson {
const json = super.toJson() as CommentDragJson;
if (this.isStart === undefined) {
throw new Error(
'Whether this event is the start of a drag is undefined. ' +
'Either pass the value to the constructor, or call fromJson',
);
}
if (this.commentId === undefined) {
throw new Error(
'The comment ID is undefined. Either pass a comment to ' +
'the constructor, or call fromJson',
);
}
json['isStart'] = this.isStart;
json['commentId'] = this.commentId;
return json;
}
/**
* Deserializes the JSON event.
*
* @param event The event to append new properties to. Should be a subclass
* of CommentDrag, but we can't specify that due to the fact that parameters
* to static methods in subclasses must be supertypes of parameters to
* static methods in superclasses.
* @internal
*/
static fromJson(
json: CommentDragJson,
workspace: Workspace,
event?: any,
): CommentDrag {
const newEvent = super.fromJson(
json,
workspace,
event ?? new CommentDrag(),
) as CommentDrag;
newEvent.isStart = json['isStart'];
newEvent.commentId = json['commentId'];
return newEvent;
}
}
export interface CommentDragJson extends AbstractEventJson {
isStart: boolean;
commentId: string;
}
registry.register(registry.Type.EVENT, EventType.COMMENT_DRAG, CommentDrag);

View File

@@ -11,19 +11,18 @@
*/
// Former goog.module ID: Blockly.Events.CommentMove
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import * as registry from '../registry.js';
import {Coordinate} from '../utils/coordinate.js';
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a workspace comment has moved.
*/
export class CommentMove extends CommentBase {
override type = eventUtils.COMMENT_MOVE;
override type = EventType.COMMENT_MOVE;
/** The comment that is being moved. */
comment_?: WorkspaceComment;
@@ -204,4 +203,4 @@ export interface CommentMoveJson extends CommentBaseJson {
newCoordinate: string;
}
registry.register(registry.Type.EVENT, eventUtils.COMMENT_MOVE, CommentMove);
registry.register(registry.Type.EVENT, EventType.COMMENT_MOVE, CommentMove);

View File

@@ -0,0 +1,169 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Class for comment resize event.
*/
import type {WorkspaceComment} from '../comments/workspace_comment.js';
import * as registry from '../registry.js';
import {Size} from '../utils/size.js';
import type {Workspace} from '../workspace.js';
import {CommentBase, CommentBaseJson} from './events_comment_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a workspace comment has resized.
*/
export class CommentResize extends CommentBase {
override type = EventType.COMMENT_RESIZE;
/** The size of the comment before the resize. */
oldSize?: Size;
/** The size of the comment after the resize. */
newSize?: Size;
/**
* @param opt_comment The comment that is being resized. Undefined for a blank
* event.
*/
constructor(opt_comment?: WorkspaceComment) {
super(opt_comment);
if (!opt_comment) {
return; // Blank event to be populated by fromJson.
}
this.oldSize = opt_comment.getSize();
}
/**
* Record the comment's new size. Called after the resize. Can only be
* called once.
*/
recordCurrentSizeAsNewSize() {
if (this.newSize) {
throw Error(
'Tried to record the new size of a comment on the ' +
'same event twice.',
);
}
const workspace = this.getEventWorkspace_();
if (!this.commentId) {
throw new Error(
'The comment ID is undefined. Either pass a comment to ' +
'the constructor, or call fromJson',
);
}
const comment = workspace.getCommentById(this.commentId);
if (!comment) {
throw new Error(
'The comment associated with the comment resize event ' +
'could not be found',
);
}
this.newSize = comment.getSize();
}
/**
* Encode the event as JSON.
*
* @returns JSON representation.
*/
override toJson(): CommentResizeJson {
const json = super.toJson() as CommentResizeJson;
if (!this.oldSize) {
throw new Error(
'The old comment size is undefined. Either pass a comment to ' +
'the constructor, or call fromJson',
);
}
if (!this.newSize) {
throw new Error(
'The new comment size is undefined. Either call ' +
'recordCurrentSizeAsNewSize, or call fromJson',
);
}
json['oldWidth'] = Math.round(this.oldSize.width);
json['oldHeight'] = Math.round(this.oldSize.height);
json['newWidth'] = Math.round(this.newSize.width);
json['newHeight'] = Math.round(this.newSize.height);
return json;
}
/**
* Deserializes the JSON event.
*
* @param event The event to append new properties to. Should be a subclass
* of CommentResize, but we can't specify that due to the fact that
* parameters to static methods in subclasses must be supertypes of
* parameters to static methods in superclasses.
* @internal
*/
static fromJson(
json: CommentResizeJson,
workspace: Workspace,
event?: any,
): CommentResize {
const newEvent = super.fromJson(
json,
workspace,
event ?? new CommentResize(),
) as CommentResize;
newEvent.oldSize = new Size(json['oldWidth'], json['oldHeight']);
newEvent.newSize = new Size(json['newWidth'], json['newHeight']);
return newEvent;
}
/**
* Does this event record any change of state?
*
* @returns False if something changed.
*/
override isNull(): boolean {
return Size.equals(this.oldSize, this.newSize);
}
/**
* Run a resize event.
*
* @param forward True if run forward, false if run backward (undo).
*/
override run(forward: boolean) {
const workspace = this.getEventWorkspace_();
if (!this.commentId) {
throw new Error(
'The comment ID is undefined. Either pass a comment to ' +
'the constructor, or call fromJson',
);
}
const comment = workspace.getCommentById(this.commentId);
if (!comment) {
console.warn("Can't resize non-existent comment: " + this.commentId);
return;
}
const size = forward ? this.newSize : this.oldSize;
if (!size) {
throw new Error(
'Either oldSize or newSize is undefined. ' +
'Either pass a comment to the constructor and call ' +
'recordCurrentSizeAsNewSize, or call fromJson',
);
}
comment.setSize(size);
}
}
export interface CommentResizeJson extends CommentBaseJson {
oldWidth: number;
oldHeight: number;
newWidth: number;
newHeight: number;
}
registry.register(registry.Type.EVENT, EventType.COMMENT_RESIZE, CommentResize);

View File

@@ -16,9 +16,8 @@ import {ASTNode} from '../keyboard_nav/ast_node.js';
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a marker (used for keyboard navigation) has
@@ -41,7 +40,7 @@ export class MarkerMove extends UiBase {
*/
isCursor?: boolean;
override type = eventUtils.MARKER_MOVE;
override type = EventType.MARKER_MOVE;
/**
* @param opt_block The affected block. Null if current node is of type
@@ -131,4 +130,4 @@ export interface MarkerMoveJson extends AbstractEventJson {
newNode: ASTNode;
}
registry.register(registry.Type.EVENT, eventUtils.MARKER_MOVE, MarkerMove);
registry.register(registry.Type.EVENT, EventType.MARKER_MOVE, MarkerMove);

View File

@@ -12,11 +12,10 @@
// Former goog.module ID: Blockly.Events.Selected
import * as registry from '../registry.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import {EventType} from './type.js';
/**
* Class for a selected event.
@@ -32,7 +31,7 @@ export class Selected extends UiBase {
*/
newElementId?: string;
override type = eventUtils.SELECTED;
override type = EventType.SELECTED;
/**
* @param opt_oldElementId The ID of the previously selected element. Null if
@@ -95,4 +94,4 @@ export interface SelectedJson extends AbstractEventJson {
newElementId?: string;
}
registry.register(registry.Type.EVENT, eventUtils.SELECTED, Selected);
registry.register(registry.Type.EVENT, EventType.SELECTED, Selected);

View File

@@ -12,10 +12,10 @@
// Former goog.module ID: Blockly.Events.ThemeChange
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {EventType} from './type.js';
/**
* Notifies listeners that the workspace theme has changed.
@@ -24,7 +24,7 @@ export class ThemeChange extends UiBase {
/** The name of the new theme that has been set. */
themeName?: string;
override type = eventUtils.THEME_CHANGE;
override type = EventType.THEME_CHANGE;
/**
* @param opt_themeName The theme name. Undefined for a blank event.
@@ -81,4 +81,4 @@ export interface ThemeChangeJson extends AbstractEventJson {
themeName: string;
}
registry.register(registry.Type.EVENT, eventUtils.THEME_CHANGE, ThemeChange);
registry.register(registry.Type.EVENT, EventType.THEME_CHANGE, ThemeChange);

View File

@@ -12,10 +12,10 @@
// Former goog.module ID: Blockly.Events.ToolboxItemSelect
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a toolbox item has been selected.
@@ -27,7 +27,7 @@ export class ToolboxItemSelect extends UiBase {
/** The newly selected toolbox item. */
newItem?: string;
override type = eventUtils.TOOLBOX_ITEM_SELECT;
override type = EventType.TOOLBOX_ITEM_SELECT;
/**
* @param opt_oldItem The previously selected toolbox item.
@@ -91,6 +91,6 @@ export interface ToolboxItemSelectJson extends AbstractEventJson {
registry.register(
registry.Type.EVENT,
eventUtils.TOOLBOX_ITEM_SELECT,
EventType.TOOLBOX_ITEM_SELECT,
ToolboxItemSelect,
);

View File

@@ -12,11 +12,10 @@
// Former goog.module ID: Blockly.Events.TrashcanOpen
import * as registry from '../registry.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners when the trashcan is opening or closing.
@@ -27,7 +26,7 @@ export class TrashcanOpen extends UiBase {
* False if it is currently closing (previously open).
*/
isOpen?: boolean;
override type = eventUtils.TRASHCAN_OPEN;
override type = EventType.TRASHCAN_OPEN;
/**
* @param opt_isOpen Whether the trashcan flyout is opening (false if
@@ -85,4 +84,4 @@ export interface TrashcanOpenJson extends AbstractEventJson {
isOpen: boolean;
}
registry.register(registry.Type.EVENT, eventUtils.TRASHCAN_OPEN, TrashcanOpen);
registry.register(registry.Type.EVENT, EventType.TRASHCAN_OPEN, TrashcanOpen);

View File

@@ -12,12 +12,11 @@
// Former goog.module ID: Blockly.Events.VarBase
import type {VariableModel} from '../variable_model.js';
import type {Workspace} from '../workspace.js';
import {
Abstract as AbstractEvent,
AbstractEventJson,
} from './events_abstract.js';
import type {Workspace} from '../workspace.js';
/**
* Abstract class for a variable event.

View File

@@ -13,16 +13,15 @@
import * as registry from '../registry.js';
import type {VariableModel} from '../variable_model.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a variable model has been created.
*/
export class VarCreate extends VarBase {
override type = eventUtils.VAR_CREATE;
override type = EventType.VAR_CREATE;
/** The type of the variable that was created. */
varType?: string;
@@ -123,4 +122,4 @@ export interface VarCreateJson extends VarBaseJson {
varName: string;
}
registry.register(registry.Type.EVENT, eventUtils.VAR_CREATE, VarCreate);
registry.register(registry.Type.EVENT, EventType.VAR_CREATE, VarCreate);

View File

@@ -8,10 +8,9 @@
import * as registry from '../registry.js';
import type {VariableModel} from '../variable_model.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a variable model has been deleted.
@@ -19,7 +18,7 @@ import type {Workspace} from '../workspace.js';
* @class
*/
export class VarDelete extends VarBase {
override type = eventUtils.VAR_DELETE;
override type = EventType.VAR_DELETE;
/** The type of the variable that was deleted. */
varType?: string;
/** The name of the variable that was deleted. */
@@ -118,4 +117,4 @@ export interface VarDeleteJson extends VarBaseJson {
varName: string;
}
registry.register(registry.Type.EVENT, eventUtils.VAR_DELETE, VarDelete);
registry.register(registry.Type.EVENT, EventType.VAR_DELETE, VarDelete);

View File

@@ -8,10 +8,9 @@
import * as registry from '../registry.js';
import type {VariableModel} from '../variable_model.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {VarBase, VarBaseJson} from './events_var_base.js';
import {EventType} from './type.js';
/**
* Notifies listeners that a variable model was renamed.
@@ -19,7 +18,7 @@ import type {Workspace} from '../workspace.js';
* @class
*/
export class VarRename extends VarBase {
override type = eventUtils.VAR_RENAME;
override type = EventType.VAR_RENAME;
/** The previous name of the variable. */
oldName?: string;
@@ -127,4 +126,4 @@ export interface VarRenameJson extends VarBaseJson {
newName: string;
}
registry.register(registry.Type.EVENT, eventUtils.VAR_RENAME, VarRename);
registry.register(registry.Type.EVENT, EventType.VAR_RENAME, VarRename);

View File

@@ -12,10 +12,10 @@
// Former goog.module ID: Blockly.Events.ViewportChange
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {AbstractEventJson} from './events_abstract.js';
import {UiBase} from './events_ui_base.js';
import * as eventUtils from './utils.js';
import type {Workspace} from '../workspace.js';
import {EventType} from './type.js';
/**
* Notifies listeners that the workspace surface's position or scale has
@@ -42,7 +42,7 @@ export class ViewportChange extends UiBase {
/** The previous scale of the workspace. */
oldScale?: number;
override type = eventUtils.VIEWPORT_CHANGE;
override type = EventType.VIEWPORT_CHANGE;
/**
* @param opt_top Top-edge of the visible portion of the workspace, relative
@@ -144,6 +144,6 @@ export interface ViewportChangeJson extends AbstractEventJson {
registry.register(
registry.Type.EVENT,
eventUtils.VIEWPORT_CHANGE,
EventType.VIEWPORT_CHANGE,
ViewportChange,
);

172
core/events/predicates.ts Normal file
View File

@@ -0,0 +1,172 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file Predicates for testing Abstract event subclasses based on
* their .type properties. These are useful because there are places
* where it is not possible to use instanceof <ClassConstructor> tests
* for type narrowing due to load ordering issues that would be caused
* by the need to import (rather than just import type) the class
* constructors in question.
*/
import type {Abstract} from './events_abstract.js';
import type {BlockChange} from './events_block_change.js';
import type {BlockCreate} from './events_block_create.js';
import type {BlockDelete} from './events_block_delete.js';
import type {BlockDrag} from './events_block_drag.js';
import type {BlockFieldIntermediateChange} from './events_block_field_intermediate_change.js';
import type {BlockMove} from './events_block_move.js';
import type {BubbleOpen} from './events_bubble_open.js';
import type {Click} from './events_click.js';
import type {CommentChange} from './events_comment_change.js';
import type {CommentCollapse} from './events_comment_collapse.js';
import type {CommentCreate} from './events_comment_create.js';
import type {CommentDelete} from './events_comment_delete.js';
import type {CommentDrag} from './events_comment_drag.js';
import type {CommentMove} from './events_comment_move.js';
import type {CommentResize} from './events_comment_resize.js';
import type {MarkerMove} from './events_marker_move.js';
import type {Selected} from './events_selected.js';
import type {ThemeChange} from './events_theme_change.js';
import type {ToolboxItemSelect} from './events_toolbox_item_select.js';
import type {TrashcanOpen} from './events_trashcan_open.js';
import type {VarCreate} from './events_var_create.js';
import type {VarDelete} from './events_var_delete.js';
import type {VarRename} from './events_var_rename.js';
import type {ViewportChange} from './events_viewport.js';
import type {FinishedLoading} from './workspace_events.js';
import {EventType} from './type.js';
/** @returns true iff event.type is EventType.BLOCK_CREATE */
export function isBlockCreate(event: Abstract): event is BlockCreate {
return event.type === EventType.BLOCK_CREATE;
}
/** @returns true iff event.type is EventType.BLOCK_DELETE */
export function isBlockDelete(event: Abstract): event is BlockDelete {
return event.type === EventType.BLOCK_DELETE;
}
/** @returns true iff event.type is EventType.BLOCK_CHANGE */
export function isBlockChange(event: Abstract): event is BlockChange {
return event.type === EventType.BLOCK_CHANGE;
}
/** @returns true iff event.type is EventType.BLOCK_FIELD_INTERMEDIATE_CHANGE */
export function isBlockFieldIntermediateChange(
event: Abstract,
): event is BlockFieldIntermediateChange {
return event.type === EventType.BLOCK_FIELD_INTERMEDIATE_CHANGE;
}
/** @returns true iff event.type is EventType.BLOCK_MOVE */
export function isBlockMove(event: Abstract): event is BlockMove {
return event.type === EventType.BLOCK_MOVE;
}
/** @returns true iff event.type is EventType.VAR_CREATE */
export function isVarCreate(event: Abstract): event is VarCreate {
return event.type === EventType.VAR_CREATE;
}
/** @returns true iff event.type is EventType.VAR_DELETE */
export function isVarDelete(event: Abstract): event is VarDelete {
return event.type === EventType.VAR_DELETE;
}
/** @returns true iff event.type is EventType.VAR_RENAME */
export function isVarRename(event: Abstract): event is VarRename {
return event.type === EventType.VAR_RENAME;
}
/** @returns true iff event.type is EventType.BLOCK_DRAG */
export function isBlockDrag(event: Abstract): event is BlockDrag {
return event.type === EventType.BLOCK_DRAG;
}
/** @returns true iff event.type is EventType.SELECTED */
export function isSelected(event: Abstract): event is Selected {
return event.type === EventType.SELECTED;
}
/** @returns true iff event.type is EventType.CLICK */
export function isClick(event: Abstract): event is Click {
return event.type === EventType.CLICK;
}
/** @returns true iff event.type is EventType.MARKER_MOVE */
export function isMarkerMove(event: Abstract): event is MarkerMove {
return event.type === EventType.MARKER_MOVE;
}
/** @returns true iff event.type is EventType.BUBBLE_OPEN */
export function isBubbleOpen(event: Abstract): event is BubbleOpen {
return event.type === EventType.BUBBLE_OPEN;
}
/** @returns true iff event.type is EventType.TRASHCAN_OPEN */
export function isTrashcanOpen(event: Abstract): event is TrashcanOpen {
return event.type === EventType.TRASHCAN_OPEN;
}
/** @returns true iff event.type is EventType.TOOLBOX_ITEM_SELECT */
export function isToolboxItemSelect(
event: Abstract,
): event is ToolboxItemSelect {
return event.type === EventType.TOOLBOX_ITEM_SELECT;
}
/** @returns true iff event.type is EventType.THEME_CHANGE */
export function isThemeChange(event: Abstract): event is ThemeChange {
return event.type === EventType.THEME_CHANGE;
}
/** @returns true iff event.type is EventType.VIEWPORT_CHANGE */
export function isViewportChange(event: Abstract): event is ViewportChange {
return event.type === EventType.VIEWPORT_CHANGE;
}
/** @returns true iff event.type is EventType.COMMENT_CREATE */
export function isCommentCreate(event: Abstract): event is CommentCreate {
return event.type === EventType.COMMENT_CREATE;
}
/** @returns true iff event.type is EventType.COMMENT_DELETE */
export function isCommentDelete(event: Abstract): event is CommentDelete {
return event.type === EventType.COMMENT_DELETE;
}
/** @returns true iff event.type is EventType.COMMENT_CHANGE */
export function isCommentChange(event: Abstract): event is CommentChange {
return event.type === EventType.COMMENT_CHANGE;
}
/** @returns true iff event.type is EventType.COMMENT_MOVE */
export function isCommentMove(event: Abstract): event is CommentMove {
return event.type === EventType.COMMENT_MOVE;
}
/** @returns true iff event.type is EventType.COMMENT_RESIZE */
export function isCommentResize(event: Abstract): event is CommentResize {
return event.type === EventType.COMMENT_RESIZE;
}
/** @returns true iff event.type is EventType.COMMENT_DRAG */
export function isCommentDrag(event: Abstract): event is CommentDrag {
return event.type === EventType.COMMENT_DRAG;
}
/** @returns true iff event.type is EventType.COMMENT_COLLAPSE */
export function isCommentCollapse(event: Abstract): event is CommentCollapse {
return event.type === EventType.COMMENT_COLLAPSE;
}
/** @returns true iff event.type is EventType.FINISHED_LOADING */
export function isFinishedLoading(event: Abstract): event is FinishedLoading {
return event.type === EventType.FINISHED_LOADING;
}

85
core/events/type.ts Normal file
View File

@@ -0,0 +1,85 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Enum of values for the .type property for event classes (concrete subclasses
* of Abstract).
*/
export enum EventType {
/** Type of event that creates a block. */
BLOCK_CREATE = 'create',
/** Type of event that deletes a block. */
BLOCK_DELETE = 'delete',
/** Type of event that changes a block. */
BLOCK_CHANGE = 'change',
/**
* Type of event representing an in-progress change to a field of a
* block, which is expected to be followed by a block change event.
*/
BLOCK_FIELD_INTERMEDIATE_CHANGE = 'block_field_intermediate_change',
/** Type of event that moves a block. */
BLOCK_MOVE = 'move',
/** Type of event that creates a variable. */
VAR_CREATE = 'var_create',
/** Type of event that deletes a variable. */
VAR_DELETE = 'var_delete',
/** Type of event that renames a variable. */
VAR_RENAME = 'var_rename',
/**
* Type of generic event that records a UI change.
*
* @deprecated Was only ever intended for internal use.
*/
UI = 'ui',
/** Type of event that drags a block. */
BLOCK_DRAG = 'drag',
/** Type of event that records a change in selected element. */
SELECTED = 'selected',
/** Type of event that records a click. */
CLICK = 'click',
/** Type of event that records a marker move. */
MARKER_MOVE = 'marker_move',
/** Type of event that records a bubble open. */
BUBBLE_OPEN = 'bubble_open',
/** Type of event that records a trashcan open. */
TRASHCAN_OPEN = 'trashcan_open',
/** Type of event that records a toolbox item select. */
TOOLBOX_ITEM_SELECT = 'toolbox_item_select',
/** Type of event that records a theme change. */
THEME_CHANGE = 'theme_change',
/** Type of event that records a viewport change. */
VIEWPORT_CHANGE = 'viewport_change',
/** Type of event that creates a comment. */
COMMENT_CREATE = 'comment_create',
/** Type of event that deletes a comment. */
COMMENT_DELETE = 'comment_delete',
/** Type of event that changes a comment. */
COMMENT_CHANGE = 'comment_change',
/** Type of event that moves a comment. */
COMMENT_MOVE = 'comment_move',
/** Type of event that resizes a comment. */
COMMENT_RESIZE = 'comment_resize',
/** Type of event that drags a comment. */
COMMENT_DRAG = 'comment_drag',
/** Type of event that collapses a comment. */
COMMENT_COLLAPSE = 'comment_collapse',
/** Type of event that records a workspace load. */
FINISHED_LOADING = 'finished_loading',
}
/**
* List of events that cause objects to be bumped back into the visible
* portion of the workspace.
*
* Not to be confused with bumping so that disconnected connections do not
* appear connected.
*/
export const BUMP_EVENTS: string[] = [
EventType.BLOCK_CREATE,
EventType.BLOCK_MOVE,
EventType.COMMENT_CREATE,
EventType.COMMENT_MOVE,
];

View File

@@ -9,17 +9,24 @@
import type {Block} from '../block.js';
import * as common from '../common.js';
import * as registry from '../registry.js';
import * as deprecation from '../utils/deprecation.js';
import * as idGenerator from '../utils/idgenerator.js';
import type {Workspace} from '../workspace.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import type {Abstract} from './events_abstract.js';
import type {BlockChange} from './events_block_change.js';
import type {BlockCreate} from './events_block_create.js';
import type {BlockMove} from './events_block_move.js';
import type {CommentCreate} from './events_comment_create.js';
import type {CommentMove} from './events_comment_move.js';
import type {ViewportChange} from './events_viewport.js';
import type {CommentResize} from './events_comment_resize.js';
import {
isBlockChange,
isBlockCreate,
isBlockMove,
isBubbleOpen,
isClick,
isViewportChange,
} from './predicates.js';
/** Group ID for new events. Grouped events are indivisible. */
let group = '';
@@ -48,146 +55,6 @@ export function getRecordUndo(): boolean {
/** Allow change events to be created and fired. */
let disabled = 0;
/**
* Name of event that creates a block. Will be deprecated for BLOCK_CREATE.
*/
export const CREATE = 'create';
/**
* Name of event that creates a block.
*/
export const BLOCK_CREATE = CREATE;
/**
* Name of event that deletes a block. Will be deprecated for BLOCK_DELETE.
*/
export const DELETE = 'delete';
/**
* Name of event that deletes a block.
*/
export const BLOCK_DELETE = DELETE;
/**
* Name of event that changes a block. Will be deprecated for BLOCK_CHANGE.
*/
export const CHANGE = 'change';
/**
* Name of event that changes a block.
*/
export const BLOCK_CHANGE = CHANGE;
/**
* Name of event representing an in-progress change to a field of a block, which
* is expected to be followed by a block change event.
*/
export const BLOCK_FIELD_INTERMEDIATE_CHANGE =
'block_field_intermediate_change';
/**
* Name of event that moves a block. Will be deprecated for BLOCK_MOVE.
*/
export const MOVE = 'move';
/**
* Name of event that moves a block.
*/
export const BLOCK_MOVE = MOVE;
/**
* Name of event that creates a variable.
*/
export const VAR_CREATE = 'var_create';
/**
* Name of event that deletes a variable.
*/
export const VAR_DELETE = 'var_delete';
/**
* Name of event that renames a variable.
*/
export const VAR_RENAME = 'var_rename';
/**
* Name of generic event that records a UI change.
*/
export const UI = 'ui';
/**
* Name of event that record a block drags a block.
*/
export const BLOCK_DRAG = 'drag';
/**
* Name of event that records a change in selected element.
*/
export const SELECTED = 'selected';
/**
* Name of event that records a click.
*/
export const CLICK = 'click';
/**
* Name of event that records a marker move.
*/
export const MARKER_MOVE = 'marker_move';
/**
* Name of event that records a bubble open.
*/
export const BUBBLE_OPEN = 'bubble_open';
/**
* Name of event that records a trashcan open.
*/
export const TRASHCAN_OPEN = 'trashcan_open';
/**
* Name of event that records a toolbox item select.
*/
export const TOOLBOX_ITEM_SELECT = 'toolbox_item_select';
/**
* Name of event that records a theme change.
*/
export const THEME_CHANGE = 'theme_change';
/**
* Name of event that records a viewport change.
*/
export const VIEWPORT_CHANGE = 'viewport_change';
/**
* Name of event that creates a comment.
*/
export const COMMENT_CREATE = 'comment_create';
/**
* Name of event that deletes a comment.
*/
export const COMMENT_DELETE = 'comment_delete';
/**
* Name of event that changes a comment.
*/
export const COMMENT_CHANGE = 'comment_change';
/**
* Name of event that moves a comment.
*/
export const COMMENT_MOVE = 'comment_move';
/** Type of event that moves a comment. */
export const COMMENT_COLLAPSE = 'comment_collapse';
/**
* Name of event that records a workspace load.
*/
export const FINISHED_LOADING = 'finished_loading';
/**
* The language-neutral ID for when the reason why a block is disabled is
* because the block is not descended from a root block.
@@ -201,29 +68,31 @@ const ORPHANED_BLOCK_DISABLED_REASON = 'ORPHANED_BLOCK';
* Not to be confused with bumping so that disconnected connections do not
* appear connected.
*/
export type BumpEvent = BlockCreate | BlockMove | CommentCreate | CommentMove;
/**
* List of events that cause objects to be bumped back into the visible
* portion of the workspace.
*
* Not to be confused with bumping so that disconnected connections do not
* appear connected.
*/
export const BUMP_EVENTS: string[] = [
BLOCK_CREATE,
BLOCK_MOVE,
COMMENT_CREATE,
COMMENT_MOVE,
];
export type BumpEvent =
| BlockCreate
| BlockMove
| CommentCreate
| CommentMove
| CommentResize;
/** List of events queued for firing. */
const FIRE_QUEUE: Abstract[] = [];
/**
* Create a custom event and fire it.
* Enqueue an event to be dispatched to change listeners.
*
* @param event Custom data for event.
* Notes:
*
* - Events are enqueued until a timeout, generally after rendering is
* complete or at the end of the current microtask, if not running
* in a browser.
* - Queued events are subject to destructive modification by being
* combined with later-enqueued events, but only until they are
* fired.
* - Events are dispatched via the fireChangeListener method on the
* affected workspace.
*
* @param event Any Blockly event.
*/
export function fire(event: Abstract) {
TEST_ONLY.fireInternal(event);
@@ -244,166 +113,187 @@ function fireInternal(event: Abstract) {
requestAnimationFrame(() => {
setTimeout(fireNow, 0);
});
} catch (e) {
} catch {
// Otherwise we just want to delay so events can be coallesced.
// requestAnimationFrame will error triggering this.
setTimeout(fireNow, 0);
}
}
FIRE_QUEUE.push(event);
enqueueEvent(event);
}
/** Fire all queued events. */
/** Dispatch all queued events. */
function fireNow() {
const queue = filter(FIRE_QUEUE, true);
FIRE_QUEUE.length = 0;
for (let i = 0, event; (event = queue[i]); i++) {
if (!event.workspaceId) {
continue;
}
const eventWorkspace = common.getWorkspaceById(event.workspaceId);
if (eventWorkspace) {
eventWorkspace.fireChangeListener(event);
}
}
// Post-filter the undo stack to squash and remove any events that result in
// a null event
// 1. Determine which workspaces will need to have their undo stacks validated
const workspaceIds = new Set(queue.map((e) => e.workspaceId));
for (const workspaceId of workspaceIds) {
// Only process valid workspaces
if (!workspaceId) {
continue;
}
const eventWorkspace = common.getWorkspaceById(workspaceId);
if (!eventWorkspace) {
continue;
}
// Find the last contiguous group of events on the stack
const undoStack = eventWorkspace.getUndoStack();
let i;
let group: string | undefined = undefined;
for (i = undoStack.length; i > 0; i--) {
const event = undoStack[i - 1];
if (event.group === '') {
break;
} else if (group === undefined) {
group = event.group;
} else if (event.group !== group) {
break;
}
}
if (!group || i == undoStack.length - 1) {
// Need a group of two or more events on the stack. Nothing to do here.
continue;
}
// Extract the event group, filter, and add back to the undo stack
let events = undoStack.splice(i, undoStack.length - i);
events = filter(events, true);
undoStack.push(...events);
for (const event of queue) {
if (!event.workspaceId) continue;
common.getWorkspaceById(event.workspaceId)?.fireChangeListener(event);
}
}
/**
* Filter the queued events and merge duplicates.
* Enqueue an event on FIRE_QUEUE.
*
* @param queueIn Array of events.
* Normally this is equivalent to FIRE_QUEUE.push(event), but if the
* enqueued event is a BlockChange event and the most recent event(s)
* on the queue are BlockMove events that (re)connect other blocks to
* the changed block (and belong to the same event group) then the
* enqueued event will be enqueued before those events rather than
* after.
*
* This is a workaround for a problem caused by the fact that
* MutatorIcon.prototype.recomposeSourceBlock can only fire a
* BlockChange event after the mutating block's compose method
* returns, meaning that if the compose method reconnects child blocks
* the corresponding BlockMove events are emitted _before_ the
* BlockChange event, causing issues with undo, mirroring, etc.; see
* https://github.com/google/blockly/issues/8225#issuecomment-2195751783
* (and following) for details.
*/
function enqueueEvent(event: Abstract) {
if (isBlockChange(event) && event.element === 'mutation') {
let i;
for (i = FIRE_QUEUE.length; i > 0; i--) {
const otherEvent = FIRE_QUEUE[i - 1];
if (
otherEvent.group !== event.group ||
otherEvent.workspaceId !== event.workspaceId ||
!isBlockMove(otherEvent) ||
otherEvent.newParentId !== event.blockId
) {
break;
}
}
FIRE_QUEUE.splice(i, 0, event);
return;
}
FIRE_QUEUE.push(event);
}
/**
* Filter the queued events by merging duplicates, removing null
* events and reording BlockChange events.
*
* History of this function:
*
* This function was originally added in commit cf257ea5 with the
* intention of dramatically reduing the total number of dispatched
* events. Initialy it affected only BlockMove events but others were
* added over time.
*
* Code was added to reorder BlockChange events added in commit
* 5578458, for uncertain reasons but most probably as part of an
* only-partially-successful attemp to fix problems with event
* ordering during block mutations. This code should probably have
* been added to the top of the function, before merging and
* null-removal, but was added at the bottom for now-forgotten
* reasons. See these bug investigations for a fuller discussion of
* the underlying issue and some of the failures that arose because of
* this incomplete/incorrect fix:
*
* https://github.com/google/blockly/issues/8225#issuecomment-2195751783
* https://github.com/google/blockly/issues/2037#issuecomment-2209696351
*
* Later, in PR #1205 the original O(n^2) implementation was replaced
* by a linear-time implementation, though additonal fixes were made
* subsequently.
*
* In August 2024 a number of significant simplifications were made:
*
* This function was previously called from Workspace.prototype.undo,
* but the mutation of events by this function was the cause of issue
* #7026 (note that events would combine differently in reverse order
* vs. forward order). The originally-chosen fix for this was the
* addition (in PR #7069) of code to fireNow to post-filter the
* .undoStack_ and .redoStack_ of any workspace that had just been
* involved in dispatching events; this apparently resolved the issue
* but added considerable additional complexity and made it difficult
* to reason about how events are processed for undo/redo, so both the
* call from undo and the post-processing code was removed, and
* forward=true was made the default while calling the function with
* forward=false was deprecated.
*
* At the same time, the buggy code to reorder BlockChange events was
* replaced by a less-buggy version of the same functionality in a new
* function, enqueueEvent, called from fireInternal, thus assuring
* that events will be in the correct order at the time filter is
* called.
*
* Additionally, the event merging code was modified so that only
* immediately adjacent events would be merged. This simplified the
* implementation while ensuring that the merging of events cannot
* cause them to be reordered.
*
* @param queue Array of events.
* @param forward True if forward (redo), false if backward (undo).
* This parameter is deprecated: true is now the default and
* calling filter with it set to false will in future not be
* supported.
* @returns Array of filtered events.
*/
export function filter(queueIn: Abstract[], forward: boolean): Abstract[] {
let queue = queueIn.slice();
// Shallow copy of queue.
export function filter(queue: Abstract[], forward = true): Abstract[] {
if (!forward) {
// Undo is merged in reverse order.
queue.reverse();
deprecation.warn('filter(queue, /*forward=*/false)', 'v11.2', 'v12');
// Undo was merged in reverse order.
queue = queue.slice().reverse(); // Copy before reversing in place.
}
const mergedQueue = [];
const hash = Object.create(null);
const mergedQueue: Abstract[] = [];
// Merge duplicates.
for (let i = 0, event; (event = queue[i]); i++) {
if (!event.isNull()) {
// Treat all UI events as the same type in hash table.
const eventType = event.isUiEvent ? UI : event.type;
// TODO(#5927): Check whether `blockId` exists before accessing it.
const blockId = (event as AnyDuringMigration).blockId;
const key = [eventType, blockId, event.workspaceId].join(' ');
const lastEntry = hash[key];
const lastEvent = lastEntry ? lastEntry.event : null;
if (!lastEntry) {
// Each item in the hash table has the event and the index of that event
// in the input array. This lets us make sure we only merge adjacent
// move events.
hash[key] = {event, index: i};
mergedQueue.push(event);
} else if (event.type === MOVE && lastEntry.index === i - 1) {
const moveEvent = event as BlockMove;
// Merge move events.
lastEvent.newParentId = moveEvent.newParentId;
lastEvent.newInputName = moveEvent.newInputName;
lastEvent.newCoordinate = moveEvent.newCoordinate;
if (moveEvent.reason) {
if (lastEvent.reason) {
// Concatenate reasons without duplicates.
const reasonSet = new Set(
moveEvent.reason.concat(lastEvent.reason),
);
lastEvent.reason = Array.from(reasonSet);
} else {
lastEvent.reason = moveEvent.reason;
}
}
lastEntry.index = i;
} else if (
event.type === CHANGE &&
(event as BlockChange).element === lastEvent.element &&
(event as BlockChange).name === lastEvent.name
) {
const changeEvent = event as BlockChange;
// Merge change events.
lastEvent.newValue = changeEvent.newValue;
} else if (event.type === VIEWPORT_CHANGE) {
const viewportEvent = event as ViewportChange;
// Merge viewport change events.
lastEvent.viewTop = viewportEvent.viewTop;
lastEvent.viewLeft = viewportEvent.viewLeft;
lastEvent.scale = viewportEvent.scale;
lastEvent.oldScale = viewportEvent.oldScale;
} else if (event.type === CLICK && lastEvent.type === BUBBLE_OPEN) {
// Drop click events caused by opening/closing bubbles.
} else {
// Collision: newer events should merge into this event to maintain
// order.
hash[key] = {event, index: i};
mergedQueue.push(event);
for (const event of queue) {
const lastEvent = mergedQueue[mergedQueue.length - 1];
if (event.isNull()) continue;
if (
!lastEvent ||
lastEvent.workspaceId !== event.workspaceId ||
lastEvent.group !== event.group
) {
mergedQueue.push(event);
continue;
}
if (
isBlockMove(event) &&
isBlockMove(lastEvent) &&
event.blockId === lastEvent.blockId
) {
// Merge move events.
lastEvent.newParentId = event.newParentId;
lastEvent.newInputName = event.newInputName;
lastEvent.newCoordinate = event.newCoordinate;
// Concatenate reasons without duplicates.
if (lastEvent.reason || event.reason) {
lastEvent.reason = Array.from(
new Set((lastEvent.reason ?? []).concat(event.reason ?? [])),
);
}
} else if (
isBlockChange(event) &&
isBlockChange(lastEvent) &&
event.blockId === lastEvent.blockId &&
event.element === lastEvent.element &&
event.name === lastEvent.name
) {
// Merge change events.
lastEvent.newValue = event.newValue;
} else if (isViewportChange(event) && isViewportChange(lastEvent)) {
// Merge viewport change events.
lastEvent.viewTop = event.viewTop;
lastEvent.viewLeft = event.viewLeft;
lastEvent.scale = event.scale;
lastEvent.oldScale = event.oldScale;
} else if (isClick(event) && isBubbleOpen(lastEvent)) {
// Drop click events caused by opening/closing bubbles.
} else {
mergedQueue.push(event);
}
}
// Filter out any events that have become null due to merging.
queue = mergedQueue.filter(function (e) {
return !e.isNull();
});
queue = mergedQueue.filter((e) => !e.isNull());
if (!forward) {
// Restore undo order.
queue.reverse();
}
// Move mutation events to the top of the queue.
// Intentionally skip first event.
for (let i = 1, event; (event = queue[i]); i++) {
// AnyDuringMigration because: Property 'element' does not exist on type
// 'Abstract'.
if (
event.type === CHANGE &&
(event as AnyDuringMigration).element === 'mutation'
) {
queue.unshift(queue.splice(i, 1)[0]);
}
}
return queue;
}
@@ -528,7 +418,7 @@ export function get(
* @param event Custom data for event.
*/
export function disableOrphans(event: Abstract) {
if (event.type === MOVE || event.type === CREATE) {
if (isBlockMove(event) || isBlockCreate(event)) {
const blockEvent = event as BlockMove | BlockCreate;
if (!blockEvent.workspaceId) {
return;
@@ -572,6 +462,7 @@ export function disableOrphans(event: Abstract) {
export const TEST_ONLY = {
FIRE_QUEUE,
enqueueEvent,
fireNow,
fireInternal,
setGroupInternal,

View File

@@ -14,7 +14,7 @@
import * as registry from '../registry.js';
import type {Workspace} from '../workspace.js';
import {Abstract as AbstractEvent} from './events_abstract.js';
import * as eventUtils from './utils.js';
import {EventType} from './type.js';
/**
* Notifies listeners when the workspace has finished deserializing from
@@ -23,7 +23,7 @@ import * as eventUtils from './utils.js';
export class FinishedLoading extends AbstractEvent {
override isBlank = true;
override recordUndo = false;
override type = eventUtils.FINISHED_LOADING;
override type = EventType.FINISHED_LOADING;
/**
* @param opt_workspace The workspace that has finished loading. Undefined
@@ -41,6 +41,6 @@ export class FinishedLoading extends AbstractEvent {
registry.register(
registry.Type.EVENT,
eventUtils.FINISHED_LOADING,
EventType.FINISHED_LOADING,
FinishedLoading,
);

View File

@@ -27,7 +27,10 @@ export const TEST_ONLY = {allExtensions};
* @throws {Error} if the extension name is empty, the extension is already
* registered, or extensionFn is not a function.
*/
export function register(name: string, initFn: Function) {
export function register<T extends Block>(
name: string,
initFn: (this: T) => void,
) {
if (typeof name !== 'string' || name.trim() === '') {
throw Error('Error: Invalid extension name "' + name + '"');
}

View File

@@ -20,12 +20,14 @@ import type {Block} from './block.js';
import type {BlockSvg} from './block_svg.js';
import * as browserEvents from './browser_events.js';
import * as dropDownDiv from './dropdowndiv.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import type {Input} from './inputs/input.js';
import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js';
import type {IASTNodeLocationWithBlock} from './interfaces/i_ast_node_location_with_block.js';
import type {IKeyboardAccessible} from './interfaces/i_keyboard_accessible.js';
import type {IRegistrable} from './interfaces/i_registrable.js';
import {ISerializable} from './interfaces/i_serializable.js';
import {MarkerManager} from './marker_manager.js';
import type {ConstantProvider} from './renderers/common/constants.js';
import type {KeyboardShortcut} from './shortcut_registry.js';
@@ -41,7 +43,6 @@ import * as userAgent from './utils/useragent.js';
import * as utilsXml from './utils/xml.js';
import * as WidgetDiv from './widgetdiv.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import {ISerializable} from './interfaces/i_serializable.js';
/**
* A function that is called to validate changes to the field's value before
@@ -1080,7 +1081,7 @@ export abstract class Field<T = any>
setValue(newValue: AnyDuringMigration, fireChangeEvent = true) {
const doLogging = false;
if (newValue === null) {
doLogging && console.log('null, return');
if (doLogging) console.log('null, return');
// Not a valid value to check.
return;
}
@@ -1092,7 +1093,7 @@ export abstract class Field<T = any>
fireChangeEvent,
);
if (classValue instanceof Error) {
doLogging && console.log('invalid class validation, return');
if (doLogging) console.log('invalid class validation, return');
return;
}
@@ -1103,19 +1104,19 @@ export abstract class Field<T = any>
fireChangeEvent,
);
if (localValue instanceof Error) {
doLogging && console.log('invalid local validation, return');
if (doLogging) console.log('invalid local validation, return');
return;
}
const source = this.sourceBlock_;
if (source && source.disposed) {
doLogging && console.log('source disposed, return');
if (doLogging) console.log('source disposed, return');
return;
}
const oldValue = this.getValue();
if (oldValue === localValue) {
doLogging && console.log('same, doValueUpdate_, return');
if (doLogging) console.log('same, doValueUpdate_, return');
this.doValueUpdate_(localValue);
return;
}
@@ -1123,7 +1124,7 @@ export abstract class Field<T = any>
this.doValueUpdate_(localValue);
if (fireChangeEvent && source && eventUtils.isEnabled()) {
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
source,
'field',
this.name || null,
@@ -1135,7 +1136,7 @@ export abstract class Field<T = any>
if (this.isDirty_) {
this.forceRerender();
}
doLogging && console.log(this.value_);
if (doLogging) console.log(this.value_);
}
/**

View File

@@ -14,9 +14,9 @@
// Unused import preserved for side-effects. Remove if unneeded.
import './events/events_block_change.js';
import * as dom from './utils/dom.js';
import {Field, FieldConfig, FieldValidator} from './field.js';
import * as fieldRegistry from './field_registry.js';
import * as dom from './utils/dom.js';
type BoolString = 'TRUE' | 'FALSE';
type CheckboxBool = BoolString | boolean;

View File

@@ -24,12 +24,12 @@ import {
import * as fieldRegistry from './field_registry.js';
import {Menu} from './menu.js';
import {MenuItem} from './menuitem.js';
import * as style from './utils/style.js';
import * as aria from './utils/aria.js';
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
import * as parsing from './utils/parsing.js';
import * as utilsString from './utils/string.js';
import * as style from './utils/style.js';
import {Svg} from './utils/svg.js';
/**

View File

@@ -15,11 +15,11 @@
import './events/events_block_change.js';
import {BlockSvg} from './block_svg.js';
import * as bumpObjects from './bump_objects.js';
import * as browserEvents from './browser_events.js';
import * as bumpObjects from './bump_objects.js';
import * as dialog from './dialog.js';
import * as dom from './utils/dom.js';
import * as dropDownDiv from './dropdowndiv.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import {
Field,
@@ -30,10 +30,11 @@ import {
import {Msg} from './msg.js';
import * as aria from './utils/aria.js';
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
import {Size} from './utils/size.js';
import * as userAgent from './utils/useragent.js';
import * as WidgetDiv from './widgetdiv.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import {Size} from './utils/size.js';
/**
* Supported types for FieldInput subclasses.
@@ -187,7 +188,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
fireChangeEvent
) {
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this.sourceBlock_,
'field',
this.name || null,
@@ -475,7 +476,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
// multiple times while the editor was open, but this will fire an event
// containing the value when the editor was opened as well as the new one.
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this.sourceBlock_,
'field',
this.name || null,
@@ -592,7 +593,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
// Fire a special event indicating that the value changed but the change
// isn't complete yet and normal field change listeners can wait.
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_FIELD_INTERMEDIATE_CHANGE))(
new (eventUtils.get(EventType.BLOCK_FIELD_INTERMEDIATE_CHANGE))(
this.sourceBlock_,
this.name || null,
oldValue,

View File

@@ -12,9 +12,9 @@
*/
// Former goog.module ID: Blockly.FieldLabel
import * as dom from './utils/dom.js';
import {Field, FieldConfig} from './field.js';
import * as fieldRegistry from './field_registry.js';
import * as dom from './utils/dom.js';
import * as parsing from './utils/parsing.js';
/**

View File

@@ -12,12 +12,12 @@
// Former goog.module ID: Blockly.FieldNumber
import {Field} from './field.js';
import * as fieldRegistry from './field_registry.js';
import {
FieldInput,
FieldInputConfig,
FieldInputValidator,
} from './field_input.js';
import * as fieldRegistry from './field_registry.js';
import * as aria from './utils/aria.js';
/**

View File

@@ -11,19 +11,22 @@
*/
// Former goog.module ID: Blockly.Flyout
import type {Abstract as AbstractEvent} from './events/events_abstract.js';
import type {Block} from './block.js';
import {BlockSvg} from './block_svg.js';
import * as browserEvents from './browser_events.js';
import * as common from './common.js';
import {ComponentManager} from './component_manager.js';
import {MANUALLY_DISABLED} from './constants.js';
import {DeleteArea} from './delete_area.js';
import type {Abstract as AbstractEvent} from './events/events_abstract.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import {FlyoutButton} from './flyout_button.js';
import {FlyoutMetricsManager} from './flyout_metrics_manager.js';
import {IAutoHideable} from './interfaces/i_autohideable.js';
import type {IFlyout} from './interfaces/i_flyout.js';
import {MANUALLY_DISABLED} from './constants.js';
import type {Options} from './options.js';
import * as renderManagement from './render_management.js';
import {ScrollbarPair} from './scrollbar_pair.js';
import * as blocks from './serialization/blocks.js';
import * as Tooltip from './tooltip.js';
@@ -32,12 +35,10 @@ import * as dom from './utils/dom.js';
import * as idGenerator from './utils/idgenerator.js';
import {Svg} from './utils/svg.js';
import * as toolbox from './utils/toolbox.js';
import * as utilsXml from './utils/xml.js';
import * as Variables from './variables.js';
import {WorkspaceSvg} from './workspace_svg.js';
import * as utilsXml from './utils/xml.js';
import * as Xml from './xml.js';
import * as renderManagement from './render_management.js';
import {IAutoHideable} from './interfaces/i_autohideable.js';
enum FlyoutItemType {
BLOCK = 'block',
@@ -427,7 +428,7 @@ export abstract class Flyout
targetWorkspace.getComponentManager().addComponent({
component: this,
weight: 1,
weight: ComponentManager.ComponentWeight.FLYOUT_WEIGHT,
capabilities: [
ComponentManager.Capability.AUTOHIDEABLE,
ComponentManager.Capability.DELETE_AREA,
@@ -1070,7 +1071,7 @@ export abstract class Flyout
* @param block The flyout block to copy.
* @returns Function to call when block is clicked.
*/
private blockMouseDown(block: BlockSvg): Function {
private blockMouseDown(block: BlockSvg) {
return (e: PointerEvent) => {
const gesture = this.targetWorkspace.getGesture(e);
if (gesture) {
@@ -1138,13 +1139,13 @@ export abstract class Flyout
for (let i = 0; i < newVariables.length; i++) {
const thisVariable = newVariables[i];
eventUtils.fire(
new (eventUtils.get(eventUtils.VAR_CREATE))(thisVariable),
new (eventUtils.get(EventType.VAR_CREATE))(thisVariable),
);
}
// Block events come after var events, in case they refer to newly created
// variables.
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(newBlock));
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_CREATE))(newBlock));
}
if (this.autoClose) {
this.hide();

View File

@@ -11,6 +11,7 @@
*/
// Former goog.module ID: Blockly.FlyoutButton
import type {IASTNodeLocationSvg} from './blockly.js';
import * as browserEvents from './browser_events.js';
import * as Css from './css.js';
import {Coordinate} from './utils/coordinate.js';
@@ -20,7 +21,6 @@ import * as style from './utils/style.js';
import {Svg} from './utils/svg.js';
import type * as toolbox from './utils/toolbox.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import type {IASTNodeLocationSvg} from './blockly.js';
/**
* Class for a button or label in the flyout.

View File

@@ -44,7 +44,7 @@ export class FlyoutMetricsManager extends MetricsManager {
let blockBoundingBox;
try {
blockBoundingBox = this.workspace_.getCanvas().getBBox();
} catch (e) {
} catch {
// Firefox has trouble with hidden elements (Bug 528969).
// 2021 Update: It looks like this was fixed around Firefox 77 released in
// 2020.

View File

@@ -295,8 +295,8 @@ export class CodeGenerator {
* @param name The name of the input.
* @param outerOrder The maximum binding strength (minimum order value) of any
* operators adjacent to "block".
* @returns Generated code or '' if no blocks are connected or the specified
* input does not exist.
* @returns Generated code or '' if no blocks are connected.
* @throws ReferenceError if the specified input does not exist.
*/
valueToCode(block: Block, name: string, outerOrder: number): string {
if (isNaN(outerOrder)) {
@@ -381,6 +381,7 @@ export class CodeGenerator {
* @param block The block containing the input.
* @param name The name of the input.
* @returns Generated code or '' if no blocks are connected.
* @throws ReferenceError if the specified input does not exist.
*/
statementToCode(block: Block, name: string): string {
const targetBlock = block.getInputTargetBlock(name);

View File

@@ -18,23 +18,24 @@ import './events/events_click.js';
import * as blockAnimations from './block_animations.js';
import type {BlockSvg} from './block_svg.js';
import * as browserEvents from './browser_events.js';
import {RenderedWorkspaceComment} from './comments.js';
import * as common from './common.js';
import {config} from './config.js';
import * as dropDownDiv from './dropdowndiv.js';
import {EventType} from './events/type.js';
import * as eventUtils from './events/utils.js';
import type {Field} from './field.js';
import type {IBubble} from './interfaces/i_bubble.js';
import {IDraggable, isDraggable} from './interfaces/i_draggable.js';
import {IDragger} from './interfaces/i_dragger.js';
import type {IFlyout} from './interfaces/i_flyout.js';
import type {IIcon} from './interfaces/i_icon.js';
import * as registry from './registry.js';
import * as Tooltip from './tooltip.js';
import * as Touch from './touch.js';
import {Coordinate} from './utils/coordinate.js';
import {WorkspaceDragger} from './workspace_dragger.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import type {IIcon} from './interfaces/i_icon.js';
import {IDragger} from './interfaces/i_dragger.js';
import * as registry from './registry.js';
import {IDraggable, isDraggable} from './interfaces/i_draggable.js';
import {RenderedWorkspaceComment} from './comments.js';
/**
* Note: In this file "start" refers to pointerdown
@@ -599,13 +600,20 @@ export class Gesture {
*/
handleTouchMove(e: PointerEvent) {
const pointerId = Touch.getTouchIdentifierFromEvent(e);
// Update the cache
this.cachedPoints.set(pointerId, this.getTouchPoint(e));
if (this.isPinchZoomEnabled && this.cachedPoints.size === 2) {
this.handlePinch(e);
} else {
this.handleMove(e);
// Handle the move directly instead of calling handleMove
this.updateFromEvent(e);
if (this.workspaceDragger) {
this.workspaceDragger.drag(this.currentDragDeltaXY);
} else if (this.dragger) {
this.dragger.onDrag(this.mostRecentEvent, this.currentDragDeltaXY);
}
e.preventDefault();
e.stopPropagation();
}
}
@@ -769,7 +777,7 @@ export class Gesture {
*/
private fireWorkspaceClick(ws: WorkspaceSvg) {
eventUtils.fire(
new (eventUtils.get(eventUtils.CLICK))(null, ws.id, 'workspace'),
new (eventUtils.get(EventType.CLICK))(null, ws.id, 'workspace'),
);
}
@@ -902,7 +910,7 @@ export class Gesture {
);
}
// Clicks events are on the start block, even if it was a shadow.
const event = new (eventUtils.get(eventUtils.CLICK))(
const event = new (eventUtils.get(EventType.CLICK))(
this.startBlock,
this.startWorkspace_.id,
'block',

View File

@@ -12,10 +12,10 @@
*/
// Former goog.module ID: Blockly.Grid
import * as dom from './utils/dom.js';
import {Coordinate} from './utils/coordinate.js';
import {Svg} from './utils/svg.js';
import {GridOptions} from './options.js';
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
import {Svg} from './utils/svg.js';
/**
* Class for a workspace's grid.

View File

@@ -4,21 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Icon} from './icons/icon.js';
import {CommentIcon, CommentState} from './icons/comment_icon.js';
import {MutatorIcon} from './icons/mutator_icon.js';
import {WarningIcon} from './icons/warning_icon.js';
import {IconType} from './icons/icon_types.js';
import * as exceptions from './icons/exceptions.js';
import {Icon} from './icons/icon.js';
import {IconType} from './icons/icon_types.js';
import {MutatorIcon} from './icons/mutator_icon.js';
import * as registry from './icons/registry.js';
import {WarningIcon} from './icons/warning_icon.js';
export {
Icon,
CommentIcon,
CommentState,
MutatorIcon,
WarningIcon,
IconType,
exceptions,
Icon,
IconType,
MutatorIcon,
registry,
WarningIcon,
};

View File

@@ -8,21 +8,22 @@
import type {Block} from '../block.js';
import type {BlockSvg} from '../block_svg.js';
import {IconType} from './icon_types.js';
import {Coordinate} from '../utils.js';
import * as dom from '../utils/dom.js';
import * as eventUtils from '../events/utils.js';
import {Icon} from './icon.js';
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
import type {ISerializable} from '../interfaces/i_serializable.js';
import {Rect} from '../utils/rect.js';
import * as registry from './registry.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import {TextBubble} from '../bubbles/text_bubble.js';
import {TextInputBubble} from '../bubbles/textinput_bubble.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
import type {ISerializable} from '../interfaces/i_serializable.js';
import * as renderManagement from '../render_management.js';
import {Coordinate} from '../utils.js';
import * as dom from '../utils/dom.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import {Icon} from './icon.js';
import {IconType} from './icon_types.js';
import * as registry from './registry.js';
/** The size of the comment icon in workspace-scale units. */
const SIZE = 17;
@@ -159,7 +160,7 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
setText(text: string) {
const oldText = this.text;
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this.sourceBlock,
'comment',
null,
@@ -238,7 +239,7 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
if (this.text === newText) return;
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this.sourceBlock,
'comment',
null,
@@ -288,7 +289,7 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
}
eventUtils.fire(
new (eventUtils.get(eventUtils.BUBBLE_OPEN))(
new (eventUtils.get(EventType.BUBBLE_OPEN))(
this.sourceBlock,
visible,
'comment',

View File

@@ -9,12 +9,12 @@ import type {BlockSvg} from '../block_svg.js';
import * as browserEvents from '../browser_events.js';
import {hasBubble} from '../interfaces/i_has_bubble.js';
import type {IIcon} from '../interfaces/i_icon.js';
import * as tooltip from '../tooltip.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import type {IconType} from './icon_types.js';
import * as tooltip from '../tooltip.js';
/**
* The abstract icon class. Icons are visual elements that live in the top-start

View File

@@ -6,22 +6,24 @@
// Former goog.module ID: Blockly.Mutator
import type {BlockSvg} from '../block_svg.js';
import type {BlocklyOptions} from '../blockly_options.js';
import {MiniWorkspaceBubble} from '../bubbles/mini_workspace_bubble.js';
import type {Abstract} from '../events/events_abstract.js';
import {BlockChange} from '../events/events_block_change.js';
import type {BlocklyOptions} from '../blockly_options.js';
import type {BlockSvg} from '../block_svg.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {isBlockChange, isBlockCreate} from '../events/predicates.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
import {Icon} from './icon.js';
import {MiniWorkspaceBubble} from '../bubbles/mini_workspace_bubble.js';
import * as renderManagement from '../render_management.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import {Icon} from './icon.js';
import {IconType} from './icon_types.js';
import * as renderManagement from '../render_management.js';
/** The size of the mutator icon in workspace-scale units. */
const SIZE = 17;
@@ -193,7 +195,7 @@ export class MutatorIcon extends Icon implements IHasBubble {
}
eventUtils.fire(
new (eventUtils.get(eventUtils.BUBBLE_OPEN))(
new (eventUtils.get(EventType.BUBBLE_OPEN))(
this.sourceBlock,
visible,
'mutator',
@@ -307,9 +309,8 @@ export class MutatorIcon extends Icon implements IHasBubble {
static isIgnorableMutatorEvent(e: Abstract) {
return (
e.isUiEvent ||
e.type === eventUtils.CREATE ||
(e.type === eventUtils.CHANGE &&
(e as BlockChange).element === 'disabled')
isBlockCreate(e) ||
(isBlockChange(e) && e.element === 'disabled')
);
}
@@ -331,7 +332,7 @@ export class MutatorIcon extends Icon implements IHasBubble {
if (oldExtraState !== newExtraState) {
eventUtils.fire(
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
new (eventUtils.get(EventType.BLOCK_CHANGE))(
this.sourceBlock,
'mutation',
null,

View File

@@ -7,17 +7,18 @@
// Former goog.module ID: Blockly.Warning
import type {BlockSvg} from '../block_svg.js';
import {TextBubble} from '../bubbles/text_bubble.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
import * as renderManagement from '../render_management.js';
import {Size} from '../utils.js';
import {Coordinate} from '../utils/coordinate.js';
import * as dom from '../utils/dom.js';
import * as eventUtils from '../events/utils.js';
import {Icon} from './icon.js';
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils.js';
import {Svg} from '../utils/svg.js';
import {TextBubble} from '../bubbles/text_bubble.js';
import {Icon} from './icon.js';
import {IconType} from './icon_types.js';
import * as renderManagement from '../render_management.js';
/** The size of the warning icon in workspace-scale units. */
const SIZE = 17;
@@ -188,7 +189,7 @@ export class WarningIcon extends Icon implements IHasBubble {
}
eventUtils.fire(
new (eventUtils.get(eventUtils.BUBBLE_OPEN))(
new (eventUtils.get(EventType.BUBBLE_OPEN))(
this.sourceBlock,
visible,
'warning',

View File

@@ -77,6 +77,8 @@ export function inject(
common.setMainWorkspace(workspace);
});
browserEvents.conditionalBind(subContainer, 'keydown', null, onKeyDown);
return workspace;
}
@@ -320,8 +322,6 @@ let documentEventsBound = false;
* Most of these events should be bound to the SVG's surface.
* However, 'mouseup' has to be on the whole document so that a block dragged
* out of bounds and released will know that it has been released.
* Also, 'keydown' has to be on the whole document since the browser doesn't
* understand a concept of focus on the SVG image.
*/
function bindDocumentEvents() {
if (!documentEventsBound) {
@@ -333,7 +333,6 @@ function bindDocumentEvents() {
}
}
});
browserEvents.conditionalBind(document, 'keydown', null, onKeyDown);
// longStop needs to run to stop the context menu from showing up. It
// should run regardless of what other touch event handlers have run.
browserEvents.bind(document, 'touchend', null, Touch.longStop);

View File

@@ -5,19 +5,19 @@
*/
import {Align} from './inputs/align.js';
import {Input} from './inputs/input.js';
import {DummyInput} from './inputs/dummy_input.js';
import {EndRowInput} from './inputs/end_row_input.js';
import {Input} from './inputs/input.js';
import {inputTypes} from './inputs/input_types.js';
import {StatementInput} from './inputs/statement_input.js';
import {ValueInput} from './inputs/value_input.js';
import {inputTypes} from './inputs/input_types.js';
export {
Align,
Input,
DummyInput,
EndRowInput,
Input,
inputTypes,
StatementInput,
ValueInput,
inputTypes,
};

View File

@@ -21,8 +21,8 @@ import type {ConnectionType} from '../connection_type.js';
import type {Field} from '../field.js';
import * as fieldRegistry from '../field_registry.js';
import type {RenderedConnection} from '../rendered_connection.js';
import {inputTypes} from './input_types.js';
import {Align} from './align.js';
import {inputTypes} from './input_types.js';
/** Class for an input with optional fields. */
export class Input {

View File

@@ -11,20 +11,20 @@
*/
// Former goog.module ID: Blockly.InsertionMarkerManager
import {finishQueuedRenders} from './render_management.js';
import * as blockAnimations from './block_animations.js';
import type {BlockSvg} from './block_svg.js';
import * as common from './common.js';
import {ComponentManager} from './component_manager.js';
import {config} from './config.js';
import * as blocks from './serialization/blocks.js';
import * as eventUtils from './events/utils.js';
import type {IDeleteArea} from './interfaces/i_delete_area.js';
import type {IDragTarget} from './interfaces/i_drag_target.js';
import * as renderManagement from './render_management.js';
import {finishQueuedRenders} from './render_management.js';
import type {RenderedConnection} from './rendered_connection.js';
import * as blocks from './serialization/blocks.js';
import type {Coordinate} from './utils/coordinate.js';
import type {WorkspaceSvg} from './workspace_svg.js';
import * as renderManagement from './render_management.js';
/** Represents a nearby valid connection. */
interface CandidateConnection {
@@ -529,7 +529,7 @@ export class InsertionMarkerManager {
local.getSourceBlock(),
local,
);
} catch (e) {
} catch {
// It's possible that the number of connections on the local block has
// changed since the insertion marker was originally created. Let's
// recreate the insertion marker and try again. In theory we could

Some files were not shown because too many files have changed in this diff Show More