mirror of
https://github.com/google/blockly.git
synced 2026-01-06 08:30:13 +01:00
release: Merge branch 'rc/v11.2.0' into rc/v12.0.0
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
# Build Artifacts
|
||||
/msg/*
|
||||
/build/*
|
||||
/dist/*
|
||||
/typings/*
|
||||
/docs/*
|
||||
|
||||
# Tests other than mocha unit tests
|
||||
/tests/blocks/*
|
||||
/tests/themes/*
|
||||
/tests/compile/*
|
||||
/tests/jsunit/*
|
||||
/tests/generators/*
|
||||
/tests/mocha/webdriver.js
|
||||
/tests/screenshot/*
|
||||
/tests/test_runner.js
|
||||
/tests/workspace_svg/*
|
||||
|
||||
# Demos, scripts, misc
|
||||
/node_modules/*
|
||||
/generators/*
|
||||
/demos/*
|
||||
/appengine/*
|
||||
/externs/*
|
||||
/closure/*
|
||||
/scripts/gulpfiles/*
|
||||
CHANGELOG.md
|
||||
PULL_REQUEST_TEMPLATE.md
|
||||
187
.eslintrc.js
187
.eslintrc.js
@@ -1,187 +0,0 @@
|
||||
const rules = {
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
'args': 'after-used',
|
||||
// Ignore vars starting with an underscore.
|
||||
'varsIgnorePattern': '^_',
|
||||
// Ignore arguments starting with an underscore.
|
||||
'argsIgnorePattern': '^_',
|
||||
},
|
||||
],
|
||||
// Blockly uses for exporting symbols. no-self-assign added in eslint 5.
|
||||
'no-self-assign': ['off'],
|
||||
// Blockly uses single quotes except for JSON blobs, which must use double
|
||||
// quotes.
|
||||
'quotes': ['off'],
|
||||
// Blockly uses 'use strict' in files.
|
||||
'strict': ['off'],
|
||||
// Closure style allows redeclarations.
|
||||
'no-redeclare': ['off'],
|
||||
'valid-jsdoc': ['error'],
|
||||
'no-console': ['off'],
|
||||
'spaced-comment': [
|
||||
'error',
|
||||
'always',
|
||||
{
|
||||
'block': {
|
||||
'balanced': true,
|
||||
},
|
||||
'exceptions': ['*'],
|
||||
},
|
||||
],
|
||||
// Blockly uses prefixes for optional arguments and test-only functions.
|
||||
'camelcase': [
|
||||
'error',
|
||||
{
|
||||
'properties': 'never',
|
||||
'allow': ['^opt_', '^_opt_', '^testOnly_'],
|
||||
},
|
||||
],
|
||||
// Blockly uses capital letters for some non-constructor namespaces.
|
||||
// Keep them for legacy reasons.
|
||||
'new-cap': ['off'],
|
||||
// Blockly uses objects as maps, but uses Object.create(null) to
|
||||
// instantiate them.
|
||||
'guard-for-in': ['off'],
|
||||
'prefer-spread': ['off'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Build shared settings for TS linting and add in the config differences.
|
||||
* @return {Object} The override TS linting for given files and a given
|
||||
* tsconfig.
|
||||
*/
|
||||
function buildTSOverride({files, tsconfig}) {
|
||||
return {
|
||||
'files': files,
|
||||
'plugins': ['@typescript-eslint/eslint-plugin', 'jsdoc'],
|
||||
'settings': {
|
||||
'jsdoc': {
|
||||
'mode': 'typescript',
|
||||
},
|
||||
},
|
||||
'parser': '@typescript-eslint/parser',
|
||||
'parserOptions': {
|
||||
'project': tsconfig,
|
||||
'tsconfigRootDir': '.',
|
||||
'ecmaVersion': 2020,
|
||||
'sourceType': 'module',
|
||||
},
|
||||
'extends': [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:jsdoc/recommended',
|
||||
'prettier', // Extend again so that these rules are applied last
|
||||
],
|
||||
'rules': {
|
||||
// TS rules
|
||||
// Blockly uses namespaces to do declaration merging in some cases.
|
||||
'@typescript-eslint/no-namespace': ['off'],
|
||||
// Use the updated TypeScript-specific rule.
|
||||
'no-invalid-this': ['off'],
|
||||
'@typescript-eslint/no-invalid-this': ['error'],
|
||||
// Needs decision. 601 problems.
|
||||
'@typescript-eslint/no-non-null-assertion': ['off'],
|
||||
// Use TS-specific rule.
|
||||
'no-unused-vars': ['off'],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
'argsIgnorePattern': '^_',
|
||||
'varsIgnorePattern': '^_',
|
||||
},
|
||||
],
|
||||
// Temporarily disable. 23 problems.
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
// Temporarily disable. 128 problems.
|
||||
'require-jsdoc': ['off'],
|
||||
// Temporarily disable. 55 problems.
|
||||
'@typescript-eslint/ban-types': ['off'],
|
||||
// Temporarily disable. 33 problems.
|
||||
'@typescript-eslint/no-empty-function': ['off'],
|
||||
// Temporarily disable. 3 problems.
|
||||
'@typescript-eslint/no-empty-interface': ['off'],
|
||||
|
||||
// TsDoc rules (using JsDoc plugin)
|
||||
// Disable built-in jsdoc verifier.
|
||||
'valid-jsdoc': ['off'],
|
||||
// Don't require types in params and returns docs.
|
||||
'jsdoc/require-param-type': ['off'],
|
||||
'jsdoc/require-returns-type': ['off'],
|
||||
// params and returns docs are optional.
|
||||
'jsdoc/require-param-description': ['off'],
|
||||
'jsdoc/require-returns': ['off'],
|
||||
// Disable for now (breaks on `this` which is not really a param).
|
||||
'jsdoc/require-param': ['off'],
|
||||
// Don't auto-add missing jsdoc. Only required on exported items.
|
||||
'jsdoc/require-jsdoc': [
|
||||
'warn',
|
||||
{
|
||||
'enableFixer': false,
|
||||
'publicOnly': true,
|
||||
},
|
||||
],
|
||||
'jsdoc/check-tag-names': [
|
||||
'error',
|
||||
{
|
||||
'definedTags': [
|
||||
'sealed',
|
||||
'typeParam',
|
||||
'remarks',
|
||||
'define',
|
||||
'nocollapse',
|
||||
'suppress',
|
||||
],
|
||||
},
|
||||
],
|
||||
// Re-enable after Closure is removed. There shouldn't even be
|
||||
// types in the TsDoc.
|
||||
// These are "types" because of Closure's @suppress {warningName}
|
||||
'jsdoc/no-undefined-types': ['off'],
|
||||
'jsdoc/valid-types': ['off'],
|
||||
// Disabled due to not handling `this`. If re-enabled,
|
||||
// checkDestructured option
|
||||
// should be left as false.
|
||||
'jsdoc/check-param-names': ['off', {'checkDestructured': false}],
|
||||
// Allow any text in the license tag. Other checks are not relevant.
|
||||
'jsdoc/check-values': ['off'],
|
||||
// Ensure there is a blank line between the body and any @tags,
|
||||
// as required by the tsdoc spec (see #6353).
|
||||
'jsdoc/tag-lines': ['error', 'any', {'startLines': 1}],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// NOTE: When this output is put directly in `module.exports`, the formatter
|
||||
// does not align with the linter.
|
||||
const eslintJSON = {
|
||||
'rules': rules,
|
||||
'env': {
|
||||
'es2020': true,
|
||||
'browser': true,
|
||||
},
|
||||
'globals': {
|
||||
'goog': true,
|
||||
'exports': true,
|
||||
},
|
||||
'extends': ['eslint:recommended', 'google', 'prettier'],
|
||||
// TypeScript-specific config. Uses above rules plus these.
|
||||
'overrides': [
|
||||
buildTSOverride({
|
||||
files: ['./**/*.ts', './**/*.tsx'],
|
||||
tsconfig: './tsconfig.json',
|
||||
}),
|
||||
buildTSOverride({
|
||||
files: ['./tests/typescript/**/*.ts', './tests/typescript/**/*.tsx'],
|
||||
tsconfig: './tests/typescript/tsconfig.json',
|
||||
}),
|
||||
{
|
||||
'files': ['./.eslintrc.js'],
|
||||
'env': {
|
||||
'node': true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = eslintJSON;
|
||||
2
.github/workflows/appengine_deploy.yml
vendored
2
.github/workflows/appengine_deploy.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
path: _deploy/
|
||||
|
||||
- name: Deploy to App Engine
|
||||
uses: google-github-actions/deploy-appengine@v2.1.2
|
||||
uses: google-github-actions/deploy-appengine@v2.1.4
|
||||
# For parameters see:
|
||||
# https://github.com/google-github-actions/deploy-appengine#inputs
|
||||
with:
|
||||
|
||||
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -18,8 +18,7 @@ jobs:
|
||||
# TODO (#2114): re-enable osx build.
|
||||
# os: [ubuntu-latest, macos-latest]
|
||||
os: [ubuntu-latest]
|
||||
# TODO(#8392): unpin v22 once npm issue fixed.
|
||||
node-version: [18.x, 20.x, 22.4.1]
|
||||
node-version: [18.x, 20.x, 22.x]
|
||||
# See supported Node.js release schedule at
|
||||
# https://nodejs.org/en/about/releases/
|
||||
|
||||
|
||||
@@ -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'],
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
155
blocks/lists.ts
155
blocks/lists.ts
@@ -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.
|
||||
@@ -412,6 +412,24 @@ const LISTS_GETINDEX = {
|
||||
this.appendDummyInput()
|
||||
.appendField(modeMenu, 'MODE')
|
||||
.appendField('', 'SPACE');
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: this.WHERE_OPTIONS,
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/** @param value The input value. */
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const oldValue: string | null = this.getValue();
|
||||
const oldAt = oldValue === 'FROM_START' || oldValue === 'FROM_END';
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
if (newAt !== oldAt) {
|
||||
const block = this.getSourceBlock() as GetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
this.appendDummyInput().appendField(menu, 'WHERE');
|
||||
this.appendDummyInput('AT');
|
||||
if (Msg['LISTS_GET_INDEX_TAIL']) {
|
||||
this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_INDEX_TAIL']);
|
||||
@@ -577,31 +595,6 @@ const LISTS_GETINDEX = {
|
||||
} else {
|
||||
this.appendDummyInput('AT');
|
||||
}
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: this.WHERE_OPTIONS,
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @returns Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the
|
||||
// replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
this.getInput('AT')!.appendField(menu, 'WHERE');
|
||||
if (Msg['LISTS_GET_INDEX_TAIL']) {
|
||||
this.moveInputBefore('TAIL', null);
|
||||
}
|
||||
@@ -644,6 +637,24 @@ const LISTS_SETINDEX = {
|
||||
this.appendDummyInput()
|
||||
.appendField(operationDropdown, 'MODE')
|
||||
.appendField('', 'SPACE');
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: this.WHERE_OPTIONS,
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/** @param value The input value. */
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const oldValue: string | null = this.getValue();
|
||||
const oldAt = oldValue === 'FROM_START' || oldValue === 'FROM_END';
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
if (newAt !== oldAt) {
|
||||
const block = this.getSourceBlock() as SetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
this.appendDummyInput().appendField(menu, 'WHERE');
|
||||
this.appendDummyInput('AT');
|
||||
this.appendValueInput('TO').appendField(Msg['LISTS_SET_INDEX_INPUT_TO']);
|
||||
this.setInputsInline(true);
|
||||
@@ -756,36 +767,10 @@ const LISTS_SETINDEX = {
|
||||
} else {
|
||||
this.appendDummyInput('AT');
|
||||
}
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: this.WHERE_OPTIONS,
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @returns Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as SetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the
|
||||
// replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
this.moveInputBefore('AT', 'TO');
|
||||
if (this.getInput('ORDINAL')) {
|
||||
this.moveInputBefore('ORDINAL', 'TO');
|
||||
}
|
||||
|
||||
this.getInput('AT')!.appendField(menu, 'WHERE');
|
||||
},
|
||||
};
|
||||
blocks['lists_setIndex'] = LISTS_SETINDEX;
|
||||
@@ -818,7 +803,30 @@ const LISTS_GETSUBLIST = {
|
||||
this.appendValueInput('LIST')
|
||||
.setCheck('Array')
|
||||
.appendField(Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']);
|
||||
const createMenu = (n: 1 | 2): FieldDropdown => {
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options:
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/** @param value The input value. */
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const oldValue: string | null = this.getValue();
|
||||
const oldAt = oldValue === 'FROM_START' || oldValue === 'FROM_END';
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
if (newAt !== oldAt) {
|
||||
const block = this.getSourceBlock() as GetSublistBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
return menu;
|
||||
};
|
||||
this.appendDummyInput('WHERE1_INPUT').appendField(createMenu(1), 'WHERE1');
|
||||
this.appendDummyInput('AT1');
|
||||
this.appendDummyInput('WHERE2_INPUT').appendField(createMenu(2), 'WHERE2');
|
||||
this.appendDummyInput('AT2');
|
||||
if (Msg['LISTS_GET_SUBLIST_TAIL']) {
|
||||
this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_SUBLIST_TAIL']);
|
||||
@@ -896,35 +904,10 @@ const LISTS_GETSUBLIST = {
|
||||
} else {
|
||||
this.appendDummyInput('AT' + n);
|
||||
}
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options:
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @returns Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetSublistBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
// This menu has been destroyed and replaced.
|
||||
// Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE' + n);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
);
|
||||
this.getInput('AT' + n)!.appendField(menu, 'WHERE' + n);
|
||||
if (n === 1) {
|
||||
this.moveInputBefore('AT1', 'AT2');
|
||||
this.moveInputBefore('AT1', 'WHERE2_INPUT');
|
||||
if (this.getInput('ORDINAL1')) {
|
||||
this.moveInputBefore('ORDINAL1', 'AT2');
|
||||
this.moveInputBefore('ORDINAL1', 'WHERE2_INPUT');
|
||||
}
|
||||
}
|
||||
if (Msg['LISTS_GET_SUBLIST_TAIL']) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -6,43 +6,43 @@
|
||||
|
||||
// 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 {Names} from '../core/names.js';
|
||||
import '../core/icons/warning_icon.js';
|
||||
import {Align} from '../core/inputs/align.js';
|
||||
import type {
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
} from '../core/interfaces/i_variable_model.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 * 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} = {};
|
||||
@@ -1108,6 +1108,14 @@ const PROCEDURE_CALL_COMMON = {
|
||||
xml.appendChild(block);
|
||||
Xml.domToWorkspace(xml, this.workspace);
|
||||
Events.setGroup(false);
|
||||
} else if (!def.isEnabled()) {
|
||||
this.setDisabledReason(
|
||||
true,
|
||||
DISABLED_PROCEDURE_DEFINITION_DISABLED_REASON,
|
||||
);
|
||||
this.setWarningText(
|
||||
Msg['PROCEDURES_CALL_DISABLED_DEF_WARNING'].replace('%1', name),
|
||||
);
|
||||
}
|
||||
} else if (event.type === Events.BLOCK_DELETE) {
|
||||
// Look for the case where a procedure definition has been deleted,
|
||||
@@ -1215,7 +1223,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_ = [];
|
||||
|
||||
@@ -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.
|
||||
@@ -216,7 +216,30 @@ const GET_SUBSTRING_BLOCK = {
|
||||
this.appendValueInput('STRING')
|
||||
.setCheck('String')
|
||||
.appendField(Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']);
|
||||
const createMenu = (n: 1 | 2): FieldDropdown => {
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options:
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/** @param value The input value. */
|
||||
function (this: FieldDropdown, value: any): null | undefined {
|
||||
const oldValue: string | null = this.getValue();
|
||||
const oldAt = oldValue === 'FROM_START' || oldValue === 'FROM_END';
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
if (newAt !== oldAt) {
|
||||
const block = this.getSourceBlock() as GetSubstringBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
return menu;
|
||||
};
|
||||
this.appendDummyInput('WHERE1_INPUT').appendField(createMenu(1), 'WHERE1');
|
||||
this.appendDummyInput('AT1');
|
||||
this.appendDummyInput('WHERE2_INPUT').appendField(createMenu(2), 'WHERE2');
|
||||
this.appendDummyInput('AT2');
|
||||
if (Msg['TEXT_GET_SUBSTRING_TAIL']) {
|
||||
this.appendDummyInput('TAIL').appendField(Msg['TEXT_GET_SUBSTRING_TAIL']);
|
||||
@@ -288,37 +311,10 @@ const GET_SUBSTRING_BLOCK = {
|
||||
this.removeInput('TAIL', true);
|
||||
this.appendDummyInput('TAIL').appendField(Msg['TEXT_GET_SUBSTRING_TAIL']);
|
||||
}
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options:
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @returns Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: any): null | undefined {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetSubstringBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
// This menu has been destroyed and replaced.
|
||||
// Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE' + n);
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
);
|
||||
|
||||
this.getInput('AT' + n)!.appendField(menu, 'WHERE' + n);
|
||||
if (n === 1) {
|
||||
this.moveInputBefore('AT1', 'AT2');
|
||||
this.moveInputBefore('AT1', 'WHERE2_INPUT');
|
||||
if (this.getInput('ORDINAL1')) {
|
||||
this.moveInputBefore('ORDINAL1', 'AT2');
|
||||
this.moveInputBefore('ORDINAL1', 'WHERE2_INPUT');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -438,6 +434,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 +469,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 +516,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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
232
core/block.ts
232
core/block.ts
@@ -23,38 +23,39 @@ 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 {
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
} from './interfaces/i_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.
|
||||
@@ -91,7 +92,7 @@ export class Block implements IASTNodeLocation {
|
||||
* Colour of the block as HSV hue value (0-360)
|
||||
* This may be null if the block colour was not set via a hue number.
|
||||
*/
|
||||
private hue_: number | null = null;
|
||||
private hue: number | null = null;
|
||||
|
||||
/** Colour of the block in '#RRGGBB' format. */
|
||||
protected colour_ = '#000000';
|
||||
@@ -146,24 +147,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;
|
||||
@@ -179,13 +187,13 @@ export class Block implements IASTNodeLocation {
|
||||
|
||||
protected childBlocks_: this[] = [];
|
||||
|
||||
private deletable_ = true;
|
||||
private deletable = true;
|
||||
|
||||
private movable_ = true;
|
||||
private movable = true;
|
||||
|
||||
private editable_ = true;
|
||||
private editable = true;
|
||||
|
||||
private isShadow_ = false;
|
||||
private shadow = false;
|
||||
|
||||
protected collapsed_ = false;
|
||||
protected outputShape_: number | null = null;
|
||||
@@ -202,7 +210,7 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
initialized = false;
|
||||
|
||||
private readonly xy_: Coordinate;
|
||||
private readonly xy: Coordinate;
|
||||
isInFlyout: boolean;
|
||||
isInMutator: boolean;
|
||||
RTL: boolean;
|
||||
@@ -219,10 +227,10 @@ 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;
|
||||
private onchangeWrapper: ((p1: Abstract) => void) | null = null;
|
||||
|
||||
/**
|
||||
* A count of statement inputs on the block.
|
||||
@@ -255,7 +263,7 @@ export class Block implements IASTNodeLocation {
|
||||
* The block's position in workspace units. (0, 0) is at the workspace's
|
||||
* origin; scale does not change this value.
|
||||
*/
|
||||
this.xy_ = new Coordinate(0, 0);
|
||||
this.xy = new Coordinate(0, 0);
|
||||
this.isInFlyout = workspace.isFlyout;
|
||||
this.isInMutator = workspace.isMutator;
|
||||
|
||||
@@ -300,7 +308,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);
|
||||
@@ -328,14 +336,14 @@ export class Block implements IASTNodeLocation {
|
||||
// Dispose of this change listener before unplugging.
|
||||
// Technically not necessary due to the event firing delay.
|
||||
// But future-proofing.
|
||||
if (this.onchangeWrapper_) {
|
||||
this.workspace.removeChangeListener(this.onchangeWrapper_);
|
||||
if (this.onchangeWrapper) {
|
||||
this.workspace.removeChangeListener(this.onchangeWrapper);
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -347,8 +355,8 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
protected disposeInternal() {
|
||||
this.disposing = true;
|
||||
if (this.onchangeWrapper_) {
|
||||
this.workspace.removeChangeListener(this.onchangeWrapper_);
|
||||
if (this.onchangeWrapper) {
|
||||
this.workspace.removeChangeListener(this.onchangeWrapper);
|
||||
}
|
||||
|
||||
this.workspace.removeTypedBlock(this);
|
||||
@@ -398,10 +406,10 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
unplug(opt_healStack?: boolean) {
|
||||
if (this.outputConnection) {
|
||||
this.unplugFromRow_(opt_healStack);
|
||||
this.unplugFromRow(opt_healStack);
|
||||
}
|
||||
if (this.previousConnection) {
|
||||
this.unplugFromStack_(opt_healStack);
|
||||
this.unplugFromStack(opt_healStack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +420,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param opt_healStack Disconnect right-side block and connect to left-side
|
||||
* block. Defaults to false.
|
||||
*/
|
||||
private unplugFromRow_(opt_healStack?: boolean) {
|
||||
private unplugFromRow(opt_healStack?: boolean) {
|
||||
let parentConnection = null;
|
||||
if (this.outputConnection?.isConnected()) {
|
||||
parentConnection = this.outputConnection.targetConnection;
|
||||
@@ -425,7 +433,7 @@ export class Block implements IASTNodeLocation {
|
||||
return;
|
||||
}
|
||||
|
||||
const thisConnection = this.getOnlyValueConnection_();
|
||||
const thisConnection = this.getOnlyValueConnection();
|
||||
if (
|
||||
!thisConnection ||
|
||||
!thisConnection.isConnected() ||
|
||||
@@ -462,7 +470,7 @@ export class Block implements IASTNodeLocation {
|
||||
*
|
||||
* @returns The connection on the value input, or null.
|
||||
*/
|
||||
private getOnlyValueConnection_(): Connection | null {
|
||||
private getOnlyValueConnection(): Connection | null {
|
||||
let connection = null;
|
||||
for (let i = 0; i < this.inputList.length; i++) {
|
||||
const thisConnection = this.inputList[i].connection;
|
||||
@@ -487,7 +495,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param opt_healStack Disconnect child statement and reconnect stack.
|
||||
* Defaults to false.
|
||||
*/
|
||||
private unplugFromStack_(opt_healStack?: boolean) {
|
||||
private unplugFromStack(opt_healStack?: boolean) {
|
||||
let previousTarget = null;
|
||||
if (this.previousConnection?.isConnected()) {
|
||||
// Remember the connection that any next statements need to connect to.
|
||||
@@ -737,14 +745,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);
|
||||
}
|
||||
|
||||
@@ -785,8 +792,8 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
isDeletable(): boolean {
|
||||
return (
|
||||
this.deletable_ &&
|
||||
!this.isShadow_ &&
|
||||
this.deletable &&
|
||||
!this.shadow &&
|
||||
!this.isDeadOrDying() &&
|
||||
!this.workspace.options.readOnly
|
||||
);
|
||||
@@ -798,7 +805,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns True if the block's deletable property is true, false otherwise.
|
||||
*/
|
||||
isOwnDeletable(): boolean {
|
||||
return this.deletable_;
|
||||
return this.deletable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,7 +814,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param deletable True if deletable.
|
||||
*/
|
||||
setDeletable(deletable: boolean) {
|
||||
this.deletable_ = deletable;
|
||||
this.deletable = deletable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -818,8 +825,8 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
isMovable(): boolean {
|
||||
return (
|
||||
this.movable_ &&
|
||||
!this.isShadow_ &&
|
||||
this.movable &&
|
||||
!this.shadow &&
|
||||
!this.isDeadOrDying() &&
|
||||
!this.workspace.options.readOnly
|
||||
);
|
||||
@@ -832,7 +839,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @internal
|
||||
*/
|
||||
isOwnMovable(): boolean {
|
||||
return this.movable_;
|
||||
return this.movable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -841,7 +848,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param movable True if movable.
|
||||
*/
|
||||
setMovable(movable: boolean) {
|
||||
this.movable_ = movable;
|
||||
this.movable = movable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -867,7 +874,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns True if a shadow.
|
||||
*/
|
||||
isShadow(): boolean {
|
||||
return this.isShadow_;
|
||||
return this.shadow;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -879,7 +886,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @internal
|
||||
*/
|
||||
setShadow(shadow: boolean) {
|
||||
this.isShadow_ = shadow;
|
||||
this.shadow = shadow;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -910,9 +917,7 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
isEditable(): boolean {
|
||||
return (
|
||||
this.editable_ &&
|
||||
!this.isDeadOrDying() &&
|
||||
!this.workspace.options.readOnly
|
||||
this.editable && !this.isDeadOrDying() && !this.workspace.options.readOnly
|
||||
);
|
||||
}
|
||||
|
||||
@@ -922,7 +927,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns True if the block's editable property is true, false otherwise.
|
||||
*/
|
||||
isOwnEditable(): boolean {
|
||||
return this.editable_;
|
||||
return this.editable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -931,7 +936,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param editable True if editable.
|
||||
*/
|
||||
setEditable(editable: boolean) {
|
||||
this.editable_ = editable;
|
||||
this.editable = editable;
|
||||
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
||||
for (let j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
field.updateEditable();
|
||||
@@ -994,7 +999,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;
|
||||
}
|
||||
|
||||
@@ -1042,7 +1047,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns Hue value (0-360).
|
||||
*/
|
||||
getHue(): number | null {
|
||||
return this.hue_;
|
||||
return this.hue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1053,7 +1058,7 @@ export class Block implements IASTNodeLocation {
|
||||
*/
|
||||
setColour(colour: number | string) {
|
||||
const parsed = parsing.parseBlockColour(colour);
|
||||
this.hue_ = parsed.hue;
|
||||
this.hue = parsed.hue;
|
||||
this.colour_ = parsed.hex;
|
||||
}
|
||||
|
||||
@@ -1079,12 +1084,12 @@ export class Block implements IASTNodeLocation {
|
||||
if (onchangeFn && typeof onchangeFn !== 'function') {
|
||||
throw Error('onchange must be a function.');
|
||||
}
|
||||
if (this.onchangeWrapper_) {
|
||||
this.workspace.removeChangeListener(this.onchangeWrapper_);
|
||||
if (this.onchangeWrapper) {
|
||||
this.workspace.removeChangeListener(this.onchangeWrapper);
|
||||
}
|
||||
this.onchange = onchangeFn;
|
||||
this.onchangeWrapper_ = onchangeFn.bind(this);
|
||||
this.workspace.addChangeListener(this.onchangeWrapper_);
|
||||
this.onchangeWrapper = onchangeFn.bind(this);
|
||||
this.workspace.addChangeListener(this.onchangeWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1326,7 +1331,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,
|
||||
@@ -1463,13 +1468,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,
|
||||
@@ -1537,7 +1561,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,
|
||||
@@ -1751,15 +1775,15 @@ export class Block implements IASTNodeLocation {
|
||||
if (json['style'] && json['colour']) {
|
||||
throw Error(warningPrefix + 'Must not have both a colour and a style.');
|
||||
} else if (json['style']) {
|
||||
this.jsonInitStyle_(json, warningPrefix);
|
||||
this.jsonInitStyle(json, warningPrefix);
|
||||
} else {
|
||||
this.jsonInitColour_(json, warningPrefix);
|
||||
this.jsonInitColour(json, warningPrefix);
|
||||
}
|
||||
|
||||
// Interpolate the message blocks.
|
||||
let i = 0;
|
||||
while (json['message' + i] !== undefined) {
|
||||
this.interpolate_(
|
||||
this.interpolate(
|
||||
json['message' + i],
|
||||
json['args' + i] || [],
|
||||
// Backwards compatibility: lastDummyAlign aliases implicitAlign.
|
||||
@@ -1834,7 +1858,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param json Structured data describing the block.
|
||||
* @param warningPrefix Warning prefix string identifying block.
|
||||
*/
|
||||
private jsonInitColour_(json: AnyDuringMigration, warningPrefix: string) {
|
||||
private jsonInitColour(json: AnyDuringMigration, warningPrefix: string) {
|
||||
if ('colour' in json) {
|
||||
if (json['colour'] === undefined) {
|
||||
console.warn(warningPrefix + 'Undefined colour value.');
|
||||
@@ -1842,7 +1866,7 @@ export class Block implements IASTNodeLocation {
|
||||
const rawValue = json['colour'];
|
||||
try {
|
||||
this.setColour(rawValue);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
console.warn(warningPrefix + 'Illegal colour value: ', rawValue);
|
||||
}
|
||||
}
|
||||
@@ -1855,11 +1879,11 @@ export class Block implements IASTNodeLocation {
|
||||
* @param json Structured data describing the block.
|
||||
* @param warningPrefix Warning prefix string identifying block.
|
||||
*/
|
||||
private jsonInitStyle_(json: AnyDuringMigration, warningPrefix: string) {
|
||||
private jsonInitStyle(json: AnyDuringMigration, warningPrefix: string) {
|
||||
const blockStyleName = json['style'];
|
||||
try {
|
||||
this.setStyle(blockStyleName);
|
||||
} catch (styleError) {
|
||||
} catch {
|
||||
console.warn(warningPrefix + 'Style does not exist: ', blockStyleName);
|
||||
}
|
||||
}
|
||||
@@ -1907,21 +1931,21 @@ export class Block implements IASTNodeLocation {
|
||||
* of newline tokens, how should it be aligned?
|
||||
* @param warningPrefix Warning prefix string identifying block.
|
||||
*/
|
||||
private interpolate_(
|
||||
private interpolate(
|
||||
message: string,
|
||||
args: AnyDuringMigration[],
|
||||
implicitAlign: string | undefined,
|
||||
warningPrefix: string,
|
||||
) {
|
||||
const tokens = parsing.tokenizeInterpolation(message);
|
||||
this.validateTokens_(tokens, args.length);
|
||||
const elements = this.interpolateArguments_(tokens, args, implicitAlign);
|
||||
this.validateTokens(tokens, args.length);
|
||||
const elements = this.interpolateArguments(tokens, args, implicitAlign);
|
||||
|
||||
// An array of [field, fieldName] tuples.
|
||||
const fieldStack = [];
|
||||
for (let i = 0, element; (element = elements[i]); i++) {
|
||||
if (this.isInputKeyword_(element['type'])) {
|
||||
const input = this.inputFromJson_(element, warningPrefix);
|
||||
if (this.isInputKeyword(element['type'])) {
|
||||
const input = this.inputFromJson(element, warningPrefix);
|
||||
// Should never be null, but just in case.
|
||||
if (input) {
|
||||
for (let j = 0, tuple; (tuple = fieldStack[j]); j++) {
|
||||
@@ -1932,7 +1956,7 @@ export class Block implements IASTNodeLocation {
|
||||
} else {
|
||||
// All other types, including ones starting with 'input_' get routed
|
||||
// here.
|
||||
const field = this.fieldFromJson_(element);
|
||||
const field = this.fieldFromJson(element);
|
||||
if (field) {
|
||||
fieldStack.push([field, element['name']]);
|
||||
}
|
||||
@@ -1948,7 +1972,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param tokens An array of tokens to validate
|
||||
* @param argsCount The number of args that need to be referred to.
|
||||
*/
|
||||
private validateTokens_(tokens: Array<string | number>, argsCount: number) {
|
||||
private validateTokens(tokens: Array<string | number>, argsCount: number) {
|
||||
const visitedArgsHash = [];
|
||||
let visitedArgsCount = 0;
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
@@ -2003,7 +2027,7 @@ export class Block implements IASTNodeLocation {
|
||||
* or dummy inputs, if necessary.
|
||||
* @returns The JSON definitions of field and inputs to add to the block.
|
||||
*/
|
||||
private interpolateArguments_(
|
||||
private interpolateArguments(
|
||||
tokens: Array<string | number>,
|
||||
args: Array<AnyDuringMigration | string>,
|
||||
implicitAlign: string | undefined,
|
||||
@@ -2026,7 +2050,7 @@ export class Block implements IASTNodeLocation {
|
||||
} else {
|
||||
// AnyDuringMigration because: Type '{ text: string; type: string; }
|
||||
// | null' is not assignable to type 'string | number'.
|
||||
element = this.stringToFieldJson_(element) as AnyDuringMigration;
|
||||
element = this.stringToFieldJson(element) as AnyDuringMigration;
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
@@ -2038,9 +2062,7 @@ export class Block implements IASTNodeLocation {
|
||||
const length = elements.length;
|
||||
if (
|
||||
length &&
|
||||
!this.isInputKeyword_(
|
||||
(elements as AnyDuringMigration)[length - 1]['type'],
|
||||
)
|
||||
!this.isInputKeyword((elements as AnyDuringMigration)[length - 1]['type'])
|
||||
) {
|
||||
const dummyInput = {'type': 'input_dummy'};
|
||||
if (implicitAlign) {
|
||||
@@ -2060,7 +2082,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param element The element to try to turn into a field.
|
||||
* @returns The field defined by the JSON, or null if one couldn't be created.
|
||||
*/
|
||||
private fieldFromJson_(element: {
|
||||
private fieldFromJson(element: {
|
||||
alt?: string;
|
||||
type: string;
|
||||
text?: string;
|
||||
@@ -2068,10 +2090,10 @@ export class Block implements IASTNodeLocation {
|
||||
const field = fieldRegistry.fromJson(element);
|
||||
if (!field && element['alt']) {
|
||||
if (typeof element['alt'] === 'string') {
|
||||
const json = this.stringToFieldJson_(element['alt']);
|
||||
return json ? this.fieldFromJson_(json) : null;
|
||||
const json = this.stringToFieldJson(element['alt']);
|
||||
return json ? this.fieldFromJson(json) : null;
|
||||
}
|
||||
return this.fieldFromJson_(element['alt']);
|
||||
return this.fieldFromJson(element['alt']);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
@@ -2086,7 +2108,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns The input that has been created, or null if one could not be
|
||||
* created for some reason (should never happen).
|
||||
*/
|
||||
private inputFromJson_(
|
||||
private inputFromJson(
|
||||
element: AnyDuringMigration,
|
||||
warningPrefix: string,
|
||||
): Input | null {
|
||||
@@ -2144,7 +2166,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns True if the given string matches one of the input keywords, false
|
||||
* otherwise.
|
||||
*/
|
||||
private isInputKeyword_(str: string): boolean {
|
||||
private isInputKeyword(str: string): boolean {
|
||||
return (
|
||||
str === 'input_value' ||
|
||||
str === 'input_statement' ||
|
||||
@@ -2161,7 +2183,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @param str String to turn into the JSON definition of a label field.
|
||||
* @returns The JSON definition or null.
|
||||
*/
|
||||
private stringToFieldJson_(str: string): {text: string; type: string} | null {
|
||||
private stringToFieldJson(str: string): {text: string; type: string} | null {
|
||||
str = str.trim();
|
||||
if (str) {
|
||||
return {
|
||||
@@ -2336,7 +2358,7 @@ export class Block implements IASTNodeLocation {
|
||||
}
|
||||
|
||||
eventUtils.fire(
|
||||
new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
|
||||
new (eventUtils.get(EventType.BLOCK_CHANGE))(
|
||||
this,
|
||||
'comment',
|
||||
null,
|
||||
@@ -2422,7 +2444,7 @@ export class Block implements IASTNodeLocation {
|
||||
* @returns Object with .x and .y properties.
|
||||
*/
|
||||
getRelativeToSurfaceXY(): Coordinate {
|
||||
return this.xy_;
|
||||
return this.xy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2436,11 +2458,9 @@ 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);
|
||||
this.xy_.translate(dx, dy);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -126,7 +145,7 @@ export class BlockSvg
|
||||
/** Block's mutator icon (if any). */
|
||||
mutator: MutatorIcon | null = null;
|
||||
|
||||
private svgGroup_: SVGGElement;
|
||||
private svgGroup: SVGGElement;
|
||||
style: BlockStyle;
|
||||
/** @internal */
|
||||
pathObject: IPathObject;
|
||||
@@ -136,15 +155,6 @@ export class BlockSvg
|
||||
|
||||
private visuallyDisabled = false;
|
||||
|
||||
/**
|
||||
* Is this block currently rendering? Used to stop recursive render calls
|
||||
* from actually triggering a re-render.
|
||||
*/
|
||||
private renderIsInProgress_ = false;
|
||||
|
||||
/** Whether mousedown events have been bound yet. */
|
||||
private eventsInit_ = false;
|
||||
|
||||
override workspace: WorkspaceSvg;
|
||||
// TODO(b/109816955): remove '!', see go/strict-prop-init-fix.
|
||||
override outputConnection!: RenderedConnection;
|
||||
@@ -182,10 +192,10 @@ export class BlockSvg
|
||||
throw TypeError('Cannot create a rendered block in a headless workspace');
|
||||
}
|
||||
this.workspace = workspace;
|
||||
this.svgGroup_ = dom.createSvgElement(Svg.G, {});
|
||||
this.svgGroup = dom.createSvgElement(Svg.G, {});
|
||||
|
||||
if (prototypeName) {
|
||||
dom.addClass(this.svgGroup_, prototypeName);
|
||||
dom.addClass(this.svgGroup, prototypeName);
|
||||
}
|
||||
/** A block style object. */
|
||||
this.style = workspace.getRenderer().getConstants().getBlockStyle(null);
|
||||
@@ -193,14 +203,14 @@ export class BlockSvg
|
||||
/** The renderer's path object. */
|
||||
this.pathObject = workspace
|
||||
.getRenderer()
|
||||
.makePathObject(this.svgGroup_, this.style);
|
||||
.makePathObject(this.svgGroup, this.style);
|
||||
|
||||
const svgPath = this.pathObject.svgPath;
|
||||
(svgPath as any).tooltip = this;
|
||||
Tooltip.bindMouseEvents(svgPath);
|
||||
|
||||
// Expose this block's ID on its top-level SVG group.
|
||||
this.svgGroup_.setAttribute('data-id', this.id);
|
||||
this.svgGroup.setAttribute('data-id', this.id);
|
||||
|
||||
this.doInit_();
|
||||
}
|
||||
@@ -222,12 +232,7 @@ export class BlockSvg
|
||||
this.pathObject.updateMovable(this.isMovable() || this.isInFlyout);
|
||||
const svg = this.getSvgRoot();
|
||||
if (!this.workspace.options.readOnly && svg) {
|
||||
browserEvents.conditionalBind(
|
||||
svg,
|
||||
'pointerdown',
|
||||
this,
|
||||
this.onMouseDown_,
|
||||
);
|
||||
browserEvents.conditionalBind(svg, 'pointerdown', this, this.onMouseDown);
|
||||
}
|
||||
|
||||
if (!svg.parentNode) {
|
||||
@@ -263,7 +268,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();
|
||||
@@ -362,8 +367,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);
|
||||
@@ -502,14 +507,14 @@ export class BlockSvg
|
||||
return;
|
||||
}
|
||||
super.setCollapsed(collapsed);
|
||||
this.updateCollapsed_();
|
||||
this.updateCollapsed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that when the block is collapsed, it is rendered correctly
|
||||
* for that state.
|
||||
*/
|
||||
private updateCollapsed_() {
|
||||
private updateCollapsed() {
|
||||
const collapsed = this.isCollapsed();
|
||||
const collapsedInputName = constants.COLLAPSED_INPUT_NAME;
|
||||
const collapsedFieldName = constants.COLLAPSED_FIELD_NAME;
|
||||
@@ -527,11 +532,11 @@ export class BlockSvg
|
||||
if (!collapsed) {
|
||||
this.updateDisabled();
|
||||
this.removeInput(collapsedInputName);
|
||||
dom.removeClass(this.svgGroup_, 'blocklyCollapsed');
|
||||
dom.removeClass(this.svgGroup, 'blocklyCollapsed');
|
||||
return;
|
||||
}
|
||||
|
||||
dom.addClass(this.svgGroup_, 'blocklyCollapsed');
|
||||
dom.addClass(this.svgGroup, 'blocklyCollapsed');
|
||||
|
||||
const text = this.toString(internalConstants.COLLAPSE_CHARS);
|
||||
const field = this.getField(collapsedFieldName);
|
||||
@@ -579,7 +584,7 @@ export class BlockSvg
|
||||
*
|
||||
* @param e Pointer down event.
|
||||
*/
|
||||
private onMouseDown_(e: PointerEvent) {
|
||||
private onMouseDown(e: PointerEvent) {
|
||||
const gesture = this.workspace.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.handleBlockStart(e, this);
|
||||
@@ -683,7 +688,7 @@ export class BlockSvg
|
||||
* @param className
|
||||
*/
|
||||
addClass(className: string) {
|
||||
dom.addClass(this.svgGroup_, className);
|
||||
dom.addClass(this.svgGroup, className);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,7 +697,7 @@ export class BlockSvg
|
||||
* @param className
|
||||
*/
|
||||
removeClass(className: string) {
|
||||
dom.removeClass(this.svgGroup_, className);
|
||||
dom.removeClass(this.svgGroup, className);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -737,9 +742,9 @@ export class BlockSvg
|
||||
super.setEditable(editable);
|
||||
|
||||
if (editable) {
|
||||
dom.removeClass(this.svgGroup_, 'blocklyNotEditable');
|
||||
dom.removeClass(this.svgGroup, 'blocklyNotEditable');
|
||||
} else {
|
||||
dom.addClass(this.svgGroup_, 'blocklyNotEditable');
|
||||
dom.addClass(this.svgGroup, 'blocklyNotEditable');
|
||||
}
|
||||
|
||||
const icons = this.getIcons();
|
||||
@@ -787,7 +792,7 @@ export class BlockSvg
|
||||
* @returns The root SVG node (probably a group).
|
||||
*/
|
||||
getSvgRoot(): SVGGElement {
|
||||
return this.svgGroup_;
|
||||
return this.svgGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -809,8 +814,27 @@ 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_);
|
||||
dom.removeNode(this.svgGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1090,9 +1114,9 @@ export class BlockSvg
|
||||
super.setDeletable(deletable);
|
||||
|
||||
if (deletable) {
|
||||
dom.removeClass(this.svgGroup_, 'blocklyNotDeletable');
|
||||
dom.removeClass(this.svgGroup, 'blocklyNotDeletable');
|
||||
} else {
|
||||
dom.addClass(this.svgGroup_, 'blocklyNotDeletable');
|
||||
dom.addClass(this.svgGroup, 'blocklyNotDeletable');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1180,7 +1204,7 @@ export class BlockSvg
|
||||
.getBlockStyle(blockStyleName);
|
||||
|
||||
if (this.styleName_) {
|
||||
dom.removeClass(this.svgGroup_, this.styleName_);
|
||||
dom.removeClass(this.svgGroup, this.styleName_);
|
||||
}
|
||||
|
||||
if (blockStyle) {
|
||||
@@ -1192,7 +1216,7 @@ export class BlockSvg
|
||||
|
||||
this.applyColour();
|
||||
|
||||
dom.addClass(this.svgGroup_, blockStyleName);
|
||||
dom.addClass(this.svgGroup, blockStyleName);
|
||||
this.styleName_ = blockStyleName;
|
||||
} else {
|
||||
throw Error('Invalid style name: ' + blockStyleName);
|
||||
@@ -1204,10 +1228,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 */
|
||||
@@ -1484,9 +1509,9 @@ export class BlockSvg
|
||||
if (conn.isConnected() && neighbour.isConnected()) continue;
|
||||
|
||||
if (conn.isSuperior()) {
|
||||
neighbour.bumpAwayFrom(conn);
|
||||
neighbour.bumpAwayFrom(conn, /* initiatedByThis = */ false);
|
||||
} else {
|
||||
conn.bumpAwayFrom(neighbour);
|
||||
conn.bumpAwayFrom(neighbour, /* initiatedByThis = */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1577,7 +1602,7 @@ export class BlockSvg
|
||||
dom.startTextWidthCache();
|
||||
|
||||
if (this.isCollapsed()) {
|
||||
this.updateCollapsed_();
|
||||
this.updateCollapsed();
|
||||
}
|
||||
|
||||
if (!this.isEnabled()) {
|
||||
|
||||
373
core/blockly.ts
373
core/blockly.ts
@@ -17,13 +17,17 @@ import './events/events_var_create.js';
|
||||
|
||||
import {Block} from './block.js';
|
||||
import * as blockAnimations from './block_animations.js';
|
||||
import {BlockFlyoutInflater} from './block_flyout_inflater.js';
|
||||
import {BlockSvg} from './block_svg.js';
|
||||
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 {ButtonFlyoutInflater} from './button_flyout_inflater.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 +35,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';
|
||||
@@ -97,22 +101,21 @@ import {
|
||||
} from './field_variable.js';
|
||||
import {Flyout, FlyoutItem} from './flyout_base.js';
|
||||
import {FlyoutButton} from './flyout_button.js';
|
||||
import {FlyoutSeparator} from './flyout_separator.js';
|
||||
import {IFlyoutInflater} from './interfaces/i_flyout_inflater.js';
|
||||
import {BlockFlyoutInflater} from './block_flyout_inflater.js';
|
||||
import {ButtonFlyoutInflater} from './button_flyout_inflater.js';
|
||||
import {LabelFlyoutInflater} from './label_flyout_inflater.js';
|
||||
import {SeparatorFlyoutInflater} from './separator_flyout_inflater.js';
|
||||
import {HorizontalFlyout} from './flyout_horizontal.js';
|
||||
import {FlyoutMetricsManager} from './flyout_metrics_manager.js';
|
||||
import {FlyoutSeparator} from './flyout_separator.js';
|
||||
import {VerticalFlyout} from './flyout_vertical.js';
|
||||
import {CodeGenerator} from './generator.js';
|
||||
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 {IFlyoutInflater} from './interfaces/i_flyout_inflater.js';
|
||||
import {LabelFlyoutInflater} from './label_flyout_inflater.js';
|
||||
import {SeparatorFlyoutInflater} from './separator_flyout_inflater.js';
|
||||
|
||||
import {Input} from './inputs/input.js';
|
||||
import {InsertionMarkerPreviewer} from './insertion_marker_previewer.js';
|
||||
import {IASTNodeLocation} from './interfaces/i_ast_node_location.js';
|
||||
import {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js';
|
||||
@@ -125,16 +128,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';
|
||||
@@ -155,35 +158,33 @@ import {ISerializable, isSerializable} from './interfaces/i_serializable.js';
|
||||
import {IStyleable} from './interfaces/i_styleable.js';
|
||||
import {IToolbox} from './interfaces/i_toolbox.js';
|
||||
import {IToolboxItem} from './interfaces/i_toolbox_item.js';
|
||||
import {IVariableMap} from './interfaces/i_variable_map.js';
|
||||
import {IVariableModel, IVariableState} from './interfaces/i_variable_model.js';
|
||||
import {
|
||||
IVariableBackedParameterModel,
|
||||
isVariableBackedParameterModel,
|
||||
} from './interfaces/i_variable_backed_parameter_model.js';
|
||||
import {IVariableMap} from './interfaces/i_variable_map.js';
|
||||
import {IVariableModel, IVariableState} from './interfaces/i_variable_model.js';
|
||||
import * as internalConstants from './internal_constants.js';
|
||||
import {ASTNode} from './keyboard_nav/ast_node.js';
|
||||
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';
|
||||
@@ -426,181 +427,205 @@ 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 {
|
||||
BlockFlyoutInflater,
|
||||
ButtonFlyoutInflater,
|
||||
CodeGenerator,
|
||||
CodeGenerator,
|
||||
Field,
|
||||
FieldCheckbox,
|
||||
FieldCheckboxConfig,
|
||||
FieldCheckboxFromJsonConfig,
|
||||
FieldCheckboxValidator,
|
||||
};
|
||||
export {
|
||||
FieldConfig,
|
||||
FieldDropdown,
|
||||
FieldDropdownConfig,
|
||||
FieldDropdownFromJsonConfig,
|
||||
FieldDropdownValidator,
|
||||
ImageProperties,
|
||||
MenuGenerator,
|
||||
MenuGeneratorFunction,
|
||||
MenuOption,
|
||||
};
|
||||
export {FieldImage, FieldImageConfig, FieldImageFromJsonConfig};
|
||||
export {FieldLabel, FieldLabelConfig, FieldLabelFromJsonConfig};
|
||||
export {FieldLabelSerializable};
|
||||
export {
|
||||
FieldImage,
|
||||
FieldImage,
|
||||
FieldImageConfig,
|
||||
FieldImageConfig,
|
||||
FieldImageFromJsonConfig,
|
||||
FieldImageFromJsonConfig,
|
||||
FieldLabel,
|
||||
FieldLabel,
|
||||
FieldLabelConfig,
|
||||
FieldLabelConfig,
|
||||
FieldLabelFromJsonConfig,
|
||||
FieldLabelFromJsonConfig,
|
||||
FieldLabelSerializable,
|
||||
FieldLabelSerializable,
|
||||
FieldNumber,
|
||||
FieldNumberConfig,
|
||||
FieldNumberFromJsonConfig,
|
||||
FieldNumberValidator,
|
||||
};
|
||||
export {
|
||||
FieldTextInput,
|
||||
FieldTextInputConfig,
|
||||
FieldTextInputFromJsonConfig,
|
||||
FieldTextInputValidator,
|
||||
};
|
||||
export {
|
||||
FieldValidator,
|
||||
FieldVariable,
|
||||
FieldVariableConfig,
|
||||
FieldVariableFromJsonConfig,
|
||||
FieldVariableValidator,
|
||||
Flyout,
|
||||
FlyoutButton,
|
||||
FlyoutItem,
|
||||
FlyoutMetricsManager,
|
||||
FlyoutSeparator,
|
||||
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,
|
||||
IFlyoutInflater,
|
||||
IHasBubble,
|
||||
IIcon,
|
||||
IKeyboardAccessible,
|
||||
IMetricsManager,
|
||||
IMovable,
|
||||
IObservable,
|
||||
IPaster,
|
||||
IPositionable,
|
||||
IRegistrable,
|
||||
IRenderedElement,
|
||||
ISelectable,
|
||||
ISelectableToolboxItem,
|
||||
ISerializable,
|
||||
IStyleable,
|
||||
IToolbox,
|
||||
IToolboxItem,
|
||||
IVariableBackedParameterModel,
|
||||
IVariableMap,
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
ImageProperties,
|
||||
Input,
|
||||
InsertionMarkerPreviewer,
|
||||
LabelFlyoutInflater,
|
||||
LayerManager,
|
||||
Marker,
|
||||
MarkerManager,
|
||||
Menu,
|
||||
MenuGenerator,
|
||||
MenuGeneratorFunction,
|
||||
MenuItem,
|
||||
MenuOption,
|
||||
MetricsManager,
|
||||
Msg,
|
||||
Names,
|
||||
Options,
|
||||
RenderedConnection,
|
||||
Scrollbar,
|
||||
ScrollbarPair,
|
||||
SeparatorFlyoutInflater,
|
||||
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, FlyoutItem};
|
||||
export {FlyoutButton};
|
||||
export {FlyoutMetricsManager};
|
||||
export {FlyoutSeparator};
|
||||
export {IFlyoutInflater};
|
||||
export {BlockFlyoutInflater};
|
||||
export {ButtonFlyoutInflater};
|
||||
export {LabelFlyoutInflater};
|
||||
export {SeparatorFlyoutInflater};
|
||||
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 {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 {IVariableMap};
|
||||
export {IVariableModel};
|
||||
export {IVariableState};
|
||||
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};
|
||||
export {layers};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
@@ -208,9 +208,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
* 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 * as drag from '../utils/drag.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.
|
||||
@@ -65,6 +66,8 @@ export class TextInputBubble extends Bubble {
|
||||
20 + Bubble.DOUBLE_BORDER,
|
||||
);
|
||||
|
||||
private editable = true;
|
||||
|
||||
/**
|
||||
* @param workspace The workspace this bubble belongs to.
|
||||
* @param anchor The anchor location of the thing this bubble is attached to.
|
||||
@@ -98,6 +101,21 @@ export class TextInputBubble extends Bubble {
|
||||
this.onTextChange();
|
||||
}
|
||||
|
||||
/** Sets whether or not the text in the bubble is editable. */
|
||||
setEditable(editable: boolean) {
|
||||
this.editable = editable;
|
||||
if (this.editable) {
|
||||
this.textArea.removeAttribute('readonly');
|
||||
} else {
|
||||
this.textArea.setAttribute('readonly', '');
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether or not the text in the bubble is editable. */
|
||||
isEditable(): boolean {
|
||||
return this.editable;
|
||||
}
|
||||
|
||||
/** Adds a change listener to be notified when this bubble's text changes. */
|
||||
addTextChangeListener(listener: () => void) {
|
||||
this.textChangeListeners.push(listener);
|
||||
@@ -247,7 +265,8 @@ export class TextInputBubble extends Bubble {
|
||||
return;
|
||||
}
|
||||
|
||||
this.workspace.startDrag(
|
||||
drag.start(
|
||||
this.workspace,
|
||||
e,
|
||||
new Coordinate(
|
||||
this.workspace.RTL ? -this.getSize().width : this.getSize().width,
|
||||
@@ -287,7 +306,7 @@ export class TextInputBubble extends Bubble {
|
||||
|
||||
/** Handles pointer move events on the resize target. */
|
||||
private onResizePointerMove(e: PointerEvent) {
|
||||
const delta = this.workspace.moveDrag(e);
|
||||
const delta = drag.move(this.workspace, e);
|
||||
this.setSize(
|
||||
new Size(this.workspace.RTL ? -delta.x : delta.x, delta.y),
|
||||
false,
|
||||
|
||||
@@ -13,7 +13,8 @@ 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 {CommentResize} from './events/events_comment_resize.js';
|
||||
import type {ViewportChange} from './events/events_viewport.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';
|
||||
@@ -99,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
|
||||
@@ -127,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);
|
||||
}
|
||||
}
|
||||
@@ -155,16 +151,16 @@ 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 eventUtils.COMMENT_RESIZE:
|
||||
case EventType.COMMENT_CREATE:
|
||||
case EventType.COMMENT_MOVE:
|
||||
case EventType.COMMENT_RESIZE:
|
||||
object = workspace.getCommentById(
|
||||
(e as CommentCreate | CommentMove | CommentResize).commentId!,
|
||||
) as RenderedWorkspaceComment;
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
// Former goog.module ID: Blockly.clipboard
|
||||
|
||||
import type {ICopyData, ICopyable} from './interfaces/i_copyable.js';
|
||||
import {BlockPaster, BlockCopyData} from './clipboard/block_paster.js';
|
||||
import * as globalRegistry from './registry.js';
|
||||
import {WorkspaceSvg} from './workspace_svg.js';
|
||||
import {BlockCopyData, BlockPaster} from './clipboard/block_paster.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;
|
||||
@@ -110,4 +110,4 @@ export const TEST_ONLY = {
|
||||
copyInternal,
|
||||
};
|
||||
|
||||
export {BlockPaster, BlockCopyData, registry};
|
||||
export {BlockCopyData, BlockPaster, registry};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
* 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 * as drag from '../utils/drag.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. */
|
||||
@@ -528,8 +529,8 @@ export class CommentView implements IRenderedElement {
|
||||
|
||||
this.preResizeSize = this.getSize();
|
||||
|
||||
// TODO(#7926): Move this into a utils file.
|
||||
this.workspace.startDrag(
|
||||
drag.start(
|
||||
this.workspace,
|
||||
e,
|
||||
new Coordinate(
|
||||
this.workspace.RTL ? -this.getSize().width : this.getSize().width,
|
||||
@@ -573,8 +574,7 @@ export class CommentView implements IRenderedElement {
|
||||
|
||||
/** 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 size = this.workspace.moveDrag(e);
|
||||
const size = drag.move(this.workspace, e);
|
||||
this.setSizeWithoutFiringEvents(
|
||||
new Size(this.workspace.RTL ? -size.x : size.x, size.y),
|
||||
);
|
||||
@@ -627,6 +627,7 @@ export class CommentView implements IRenderedElement {
|
||||
* event on the foldout icon.
|
||||
*/
|
||||
private onFoldoutDown(e: PointerEvent) {
|
||||
touch.clearTouchIdentifier();
|
||||
this.bringToFront();
|
||||
if (browserEvents.isRightButton(e)) {
|
||||
e.stopPropagation();
|
||||
@@ -747,6 +748,7 @@ export class CommentView implements IRenderedElement {
|
||||
* delete icon.
|
||||
*/
|
||||
private onDeleteDown(e: PointerEvent) {
|
||||
touch.clearTouchIdentifier();
|
||||
if (browserEvents.isRightButton(e)) {
|
||||
e.stopPropagation();
|
||||
return;
|
||||
@@ -836,7 +838,6 @@ css.register(`
|
||||
}
|
||||
|
||||
.blocklyCommentTopbarBackground {
|
||||
cursor: grab;
|
||||
fill: var(--commentBorderColour);
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -213,6 +214,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {Workspace} from '../workspace.js';
|
||||
import {Size} from '../utils/size.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 {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 {Size} from '../utils/size.js';
|
||||
import {Workspace} from '../workspace.js';
|
||||
import {CommentView} from './comment_view.js';
|
||||
|
||||
export class WorkspaceComment {
|
||||
@@ -65,13 +66,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +80,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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -88,7 +89,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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -107,7 +108,7 @@ export class WorkspaceComment {
|
||||
|
||||
/** Sets the comment's size in workspace units. */
|
||||
setSize(size: Size) {
|
||||
const event = new (eventUtils.get(eventUtils.COMMENT_RESIZE))(
|
||||
const event = new (eventUtils.get(EventType.COMMENT_RESIZE))(
|
||||
this,
|
||||
) as CommentResize;
|
||||
|
||||
@@ -185,7 +186,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
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +203,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);
|
||||
|
||||
@@ -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 ?? '',
|
||||
|
||||
@@ -23,10 +23,10 @@ class Capability<_T> {
|
||||
static DRAG_TARGET = new Capability<IDragTarget>('drag_target');
|
||||
static DELETE_AREA = new Capability<IDeleteArea>('delete_area');
|
||||
static AUTOHIDEABLE = new Capability<IAutoHideable>('autohideable');
|
||||
private readonly name_: string;
|
||||
private readonly name: string;
|
||||
/** @param name The name of the component capability. */
|
||||
constructor(name: string) {
|
||||
this.name_ = name;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,7 @@ class Capability<_T> {
|
||||
* @returns The name.
|
||||
*/
|
||||
toString(): string {
|
||||
return this.name_;
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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']);
|
||||
@@ -213,11 +214,11 @@ export class Connection implements IASTNodeLocationWithBlock {
|
||||
* Called when an attempted connection fails. NOP by default (i.e. for
|
||||
* headless workspaces).
|
||||
*
|
||||
* @param _otherConnection Connection that this connection failed to connect
|
||||
* to.
|
||||
* @param _superiorConnection Connection that this connection failed to connect
|
||||
* to. The provided connection should be the superior connection.
|
||||
* @internal
|
||||
*/
|
||||
onFailedConnect(_otherConnection: Connection) {}
|
||||
onFailedConnect(_superiorConnection: Connection) {}
|
||||
// NOP
|
||||
|
||||
/**
|
||||
@@ -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']);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {WorkspaceSvg} from './workspace_svg.js';
|
||||
export class ContextMenuRegistry {
|
||||
static registry: ContextMenuRegistry;
|
||||
/** Registry of all registered RegistryItems, keyed by ID. */
|
||||
private registry_ = new Map<string, RegistryItem>();
|
||||
private registeredItems = new Map<string, RegistryItem>();
|
||||
|
||||
/** Resets the existing singleton instance of ContextMenuRegistry. */
|
||||
constructor() {
|
||||
@@ -32,7 +32,7 @@ export class ContextMenuRegistry {
|
||||
|
||||
/** Clear and recreate the registry. */
|
||||
reset() {
|
||||
this.registry_.clear();
|
||||
this.registeredItems.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,10 +42,10 @@ export class ContextMenuRegistry {
|
||||
* @throws {Error} if an item with the given ID already exists.
|
||||
*/
|
||||
register(item: RegistryItem) {
|
||||
if (this.registry_.has(item.id)) {
|
||||
if (this.registeredItems.has(item.id)) {
|
||||
throw Error('Menu item with ID "' + item.id + '" is already registered.');
|
||||
}
|
||||
this.registry_.set(item.id, item);
|
||||
this.registeredItems.set(item.id, item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,10 +55,10 @@ export class ContextMenuRegistry {
|
||||
* @throws {Error} if an item with the given ID does not exist.
|
||||
*/
|
||||
unregister(id: string) {
|
||||
if (!this.registry_.has(id)) {
|
||||
if (!this.registeredItems.has(id)) {
|
||||
throw new Error('Menu item with ID "' + id + '" not found.');
|
||||
}
|
||||
this.registry_.delete(id);
|
||||
this.registeredItems.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ export class ContextMenuRegistry {
|
||||
* @returns RegistryItem or null if not found
|
||||
*/
|
||||
getItem(id: string): RegistryItem | null {
|
||||
return this.registry_.get(id) ?? null;
|
||||
return this.registeredItems.get(id) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ export class ContextMenuRegistry {
|
||||
scope: Scope,
|
||||
): ContextMenuOption[] {
|
||||
const menuOptions: ContextMenuOption[] = [];
|
||||
for (const item of this.registry_.values()) {
|
||||
for (const item of this.registeredItems.values()) {
|
||||
if (scopeType === item.scopeType) {
|
||||
const precondition = item.preconditionFn(scope);
|
||||
if (precondition !== 'hidden') {
|
||||
|
||||
@@ -78,6 +78,8 @@ let content = `
|
||||
position: relative;
|
||||
overflow: hidden; /* So blocks in drag surface disappear at edges */
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.blocklyBlockCanvas.blocklyCanvasTransitioning,
|
||||
@@ -214,10 +216,6 @@ let content = `
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.blocklyMultilineText {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.blocklyNonEditableField>text {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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']);
|
||||
@@ -379,17 +384,24 @@ export class BlockDragStrategy implements IDragStrategy {
|
||||
if (this.connectionCandidate) {
|
||||
// Applying connections also rerenders the relevant blocks.
|
||||
this.applyConnections(this.connectionCandidate);
|
||||
this.disposeStep();
|
||||
} else {
|
||||
this.block.queueRender();
|
||||
this.block.queueRender().then(() => this.disposeStep());
|
||||
}
|
||||
|
||||
if (!this.inGroup) {
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Disposes of any state at the end of the drag. */
|
||||
private disposeStep() {
|
||||
this.block.snapToGrid();
|
||||
|
||||
// Must dispose after connections are applied to not break the dynamic
|
||||
// connections plugin. See #7859
|
||||
this.connectionPreviewer!.dispose();
|
||||
this.workspace.setResizesEnabled(true);
|
||||
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
|
||||
/** Connects the given candidate connections. */
|
||||
|
||||
@@ -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);
|
||||
if (!this.inGroup) {
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
|
||||
this.workspace
|
||||
.getLayerManager()
|
||||
|
||||
@@ -4,29 +4,38 @@
|
||||
* 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();
|
||||
@@ -52,12 +61,14 @@ export class CommentDragStrategy implements IDragStrategy {
|
||||
this.comment.snapToGrid();
|
||||
|
||||
this.workspace.setResizesEnabled(true);
|
||||
if (!this.inGroup) {
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Fire a UI event at the start of a comment drag. */
|
||||
private fireDragStartEvent() {
|
||||
const event = new (eventUtils.get(eventUtils.COMMENT_DRAG))(
|
||||
const event = new (eventUtils.get(EventType.COMMENT_DRAG))(
|
||||
this.comment,
|
||||
true,
|
||||
);
|
||||
@@ -66,7 +77,7 @@ export class CommentDragStrategy implements IDragStrategy {
|
||||
|
||||
/** Fire a UI event at the end of a comment drag. */
|
||||
private fireDragEndEvent() {
|
||||
const event = new (eventUtils.get(eventUtils.COMMENT_DRAG))(
|
||||
const event = new (eventUtils.get(EventType.COMMENT_DRAG))(
|
||||
this.comment,
|
||||
false,
|
||||
);
|
||||
@@ -76,7 +87,7 @@ export class CommentDragStrategy implements IDragStrategy {
|
||||
/** 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']);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = '';
|
||||
@@ -196,7 +196,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(
|
||||
@@ -220,7 +220,7 @@ export function showPositionedByBlock<T>(
|
||||
*/
|
||||
export function showPositionedByField<T>(
|
||||
field: Field<T>,
|
||||
opt_onHide?: Function,
|
||||
opt_onHide?: () => void,
|
||||
opt_secondaryYOffset?: number,
|
||||
): boolean {
|
||||
positionToField = true;
|
||||
@@ -272,7 +272,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.
|
||||
@@ -328,7 +328,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;
|
||||
|
||||
@@ -6,158 +6,105 @@
|
||||
|
||||
// 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 {CommentResize, CommentResizeJson} from './events_comment_resize.js';
|
||||
import {CommentDrag, CommentDragJson} from './events_comment_drag.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 {VarTypeChange, VarTypeChangeJson} from './events_var_type_change.js';
|
||||
import {ViewportChange, ViewportChangeJson} from './events_viewport.js';
|
||||
import * as eventUtils from './utils.js';
|
||||
import {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 {CommentResize};
|
||||
export {CommentResizeJson};
|
||||
export {CommentDrag};
|
||||
export {CommentDragJson};
|
||||
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 {VarTypeChange};
|
||||
export {VarTypeChangeJson};
|
||||
export {ViewportChange};
|
||||
export {ViewportChangeJson};
|
||||
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';
|
||||
|
||||
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 COMMENT_RESIZE = eventUtils.COMMENT_RESIZE;
|
||||
export const COMMENT_DRAG = eventUtils.COMMENT_DRAG;
|
||||
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 VAR_TYPE_CHAGE = eventUtils.VAR_TYPE_CHANGE;
|
||||
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';
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
import type {Block} from '../block.js';
|
||||
import type {Workspace} from '../workspace.js';
|
||||
|
||||
import {
|
||||
Abstract as AbstractEvent,
|
||||
AbstractEventJson,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -90,7 +89,7 @@ export class BlockMove extends BlockBase {
|
||||
this.recordUndo = false;
|
||||
}
|
||||
|
||||
const location = this.currentLocation_();
|
||||
const location = this.currentLocation();
|
||||
this.oldParentId = location.parentId;
|
||||
this.oldInputName = location.inputName;
|
||||
this.oldCoordinate = location.coordinate;
|
||||
@@ -168,7 +167,7 @@ export class BlockMove extends BlockBase {
|
||||
|
||||
/** Record the block's new location. Called after the move. */
|
||||
recordNew() {
|
||||
const location = this.currentLocation_();
|
||||
const location = this.currentLocation();
|
||||
this.newParentId = location.parentId;
|
||||
this.newInputName = location.inputName;
|
||||
this.newCoordinate = location.coordinate;
|
||||
@@ -189,7 +188,7 @@ export class BlockMove extends BlockBase {
|
||||
*
|
||||
* @returns Collection of location info.
|
||||
*/
|
||||
private currentLocation_(): BlockLocation {
|
||||
private currentLocation(): BlockLocation {
|
||||
const workspace = this.getEventWorkspace_();
|
||||
if (!this.blockId) {
|
||||
throw new Error(
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
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 * as eventUtils from './utils.js';
|
||||
import {Workspace} from '../workspace.js';
|
||||
import {EventType} from './type.js';
|
||||
|
||||
/**
|
||||
* Notifies listeners when a comment is being manually dragged/dropped.
|
||||
@@ -25,7 +25,7 @@ export class CommentDrag extends UiBase {
|
||||
/** True if this is the start of a drag, false if this is the end of one. */
|
||||
isStart?: boolean;
|
||||
|
||||
override type = eventUtils.COMMENT_DRAG;
|
||||
override type = EventType.COMMENT_DRAG;
|
||||
|
||||
/**
|
||||
* @param opt_comment The comment that is being dragged.
|
||||
@@ -96,4 +96,4 @@ export interface CommentDragJson extends AbstractEventJson {
|
||||
commentId: string;
|
||||
}
|
||||
|
||||
registry.register(registry.Type.EVENT, eventUtils.COMMENT_DRAG, CommentDrag);
|
||||
registry.register(registry.Type.EVENT, EventType.COMMENT_DRAG, CommentDrag);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,19 +8,18 @@
|
||||
* 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 {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 resized.
|
||||
*/
|
||||
export class CommentResize extends CommentBase {
|
||||
override type = eventUtils.COMMENT_RESIZE;
|
||||
override type = EventType.COMMENT_RESIZE;
|
||||
|
||||
/** The size of the comment before the resize. */
|
||||
oldSize?: Size;
|
||||
@@ -167,8 +166,4 @@ export interface CommentResizeJson extends CommentBaseJson {
|
||||
newHeight: number;
|
||||
}
|
||||
|
||||
registry.register(
|
||||
registry.Type.EVENT,
|
||||
eventUtils.COMMENT_RESIZE,
|
||||
CommentResize,
|
||||
);
|
||||
registry.register(registry.Type.EVENT, EventType.COMMENT_RESIZE, CommentResize);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -15,12 +15,11 @@ import type {
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
} from '../interfaces/i_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.
|
||||
|
||||
@@ -11,21 +11,21 @@
|
||||
*/
|
||||
// Former goog.module ID: Blockly.Events.VarCreate
|
||||
|
||||
import * as registry from '../registry.js';
|
||||
import type {
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
} from '../interfaces/i_variable_model.js';
|
||||
import * as registry from '../registry.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;
|
||||
@@ -126,4 +126,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);
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
|
||||
// Former goog.module ID: Blockly.Events.VarDelete
|
||||
|
||||
import * as registry from '../registry.js';
|
||||
import type {
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
} from '../interfaces/i_variable_model.js';
|
||||
import * as registry from '../registry.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.
|
||||
@@ -22,7 +22,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. */
|
||||
@@ -121,4 +121,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);
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
|
||||
// Former goog.module ID: Blockly.Events.VarRename
|
||||
|
||||
import * as registry from '../registry.js';
|
||||
import type {
|
||||
IVariableModel,
|
||||
IVariableState,
|
||||
} from '../interfaces/i_variable_model.js';
|
||||
import * as registry from '../registry.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.
|
||||
@@ -22,7 +22,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;
|
||||
@@ -130,4 +130,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);
|
||||
|
||||
@@ -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
172
core/events/predicates.ts
Normal 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
85
core/events/type.ts
Normal 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,
|
||||
];
|
||||
@@ -9,18 +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 {CommentResize} from './events_comment_resize.js';
|
||||
import type {ViewportChange} from './events_viewport.js';
|
||||
import {
|
||||
isBlockChange,
|
||||
isBlockCreate,
|
||||
isBlockMove,
|
||||
isBubbleOpen,
|
||||
isClick,
|
||||
isViewportChange,
|
||||
} from './predicates.js';
|
||||
|
||||
/** Group ID for new events. Grouped events are indivisible. */
|
||||
let group = '';
|
||||
@@ -49,157 +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 event that changes a variable's type.
|
||||
*/
|
||||
export const VAR_TYPE_CHANGE = 'var_type_change';
|
||||
|
||||
/**
|
||||
* Name of generic event that records a UI change.
|
||||
*/
|
||||
export const UI = 'ui';
|
||||
|
||||
/**
|
||||
* Name of event that 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';
|
||||
|
||||
/** Name of event that resizes a comment. */
|
||||
export const COMMENT_RESIZE = 'comment_resize';
|
||||
|
||||
/** Name of event that drags a comment. */
|
||||
export const COMMENT_DRAG = 'comment_drag';
|
||||
|
||||
/** Type of event that collapses 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.
|
||||
@@ -220,27 +75,24 @@ export type BumpEvent =
|
||||
| CommentMove
|
||||
| CommentResize;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
];
|
||||
|
||||
/** 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);
|
||||
@@ -261,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
|
||||
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
|
||||
) {
|
||||
const changeEvent = event as BlockChange;
|
||||
// Merge change events.
|
||||
lastEvent.newValue = changeEvent.newValue;
|
||||
} else if (event.type === VIEWPORT_CHANGE) {
|
||||
const viewportEvent = event as ViewportChange;
|
||||
lastEvent.newValue = event.newValue;
|
||||
} else if (isViewportChange(event) && isViewportChange(lastEvent)) {
|
||||
// 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) {
|
||||
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 {
|
||||
// Collision: newer events should merge into this event to maintain
|
||||
// order.
|
||||
hash[key] = {event, index: i};
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -545,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;
|
||||
@@ -589,6 +462,7 @@ export function disableOrphans(event: Abstract) {
|
||||
|
||||
export const TEST_ONLY = {
|
||||
FIRE_QUEUE,
|
||||
enqueueEvent,
|
||||
fireNow,
|
||||
fireInternal,
|
||||
setGroupInternal,
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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 + '"');
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -106,20 +107,20 @@ export abstract class Field<T = any>
|
||||
* Used to cache the field's tooltip value if setTooltip is called when the
|
||||
* field is not yet initialized. Is *not* guaranteed to be accurate.
|
||||
*/
|
||||
private tooltip_: Tooltip.TipInfo | null = null;
|
||||
private tooltip: Tooltip.TipInfo | null = null;
|
||||
protected size_: Size;
|
||||
|
||||
/**
|
||||
* Holds the cursors svg element when the cursor is attached to the field.
|
||||
* This is null if there is no cursor on the field.
|
||||
*/
|
||||
private cursorSvg_: SVGElement | null = null;
|
||||
private cursorSvg: SVGElement | null = null;
|
||||
|
||||
/**
|
||||
* Holds the markers svg element when the marker is attached to the field.
|
||||
* This is null if there is no marker on the field.
|
||||
*/
|
||||
private markerSvg_: SVGElement | null = null;
|
||||
private markerSvg: SVGElement | null = null;
|
||||
|
||||
/** The rendered field's SVG group element. */
|
||||
protected fieldGroup_: SVGGElement | null = null;
|
||||
@@ -134,7 +135,7 @@ export abstract class Field<T = any>
|
||||
protected textContent_: Text | null = null;
|
||||
|
||||
/** Mouse down event listener data. */
|
||||
private mouseDownWrapper_: browserEvents.Data | null = null;
|
||||
private mouseDownWrapper: browserEvents.Data | null = null;
|
||||
|
||||
/** Constants associated with the source block's renderer. */
|
||||
protected constants_: ConstantProvider | null = null;
|
||||
@@ -308,7 +309,7 @@ export abstract class Field<T = any>
|
||||
sourceBlockSvg.getSvgRoot().appendChild(this.fieldGroup_);
|
||||
this.initView();
|
||||
this.updateEditable();
|
||||
this.setTooltip(this.tooltip_);
|
||||
this.setTooltip(this.tooltip);
|
||||
this.bindEvents_();
|
||||
this.initModel();
|
||||
this.applyColour();
|
||||
@@ -392,7 +393,7 @@ export abstract class Field<T = any>
|
||||
const clickTarget = this.getClickTarget_();
|
||||
if (!clickTarget) throw new Error('A click target has not been set.');
|
||||
Tooltip.bindMouseEvents(clickTarget);
|
||||
this.mouseDownWrapper_ = browserEvents.conditionalBind(
|
||||
this.mouseDownWrapper = browserEvents.conditionalBind(
|
||||
clickTarget,
|
||||
'pointerdown',
|
||||
this,
|
||||
@@ -1065,42 +1066,50 @@ 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;
|
||||
}
|
||||
|
||||
// Field validators are allowed to make changes to the workspace, which
|
||||
// should get grouped with the field value change event.
|
||||
const existingGroup = eventUtils.getGroup();
|
||||
if (!existingGroup) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
|
||||
try {
|
||||
const classValidation = this.doClassValidation_(newValue);
|
||||
const classValue = this.processValidation_(
|
||||
const classValue = this.processValidation(
|
||||
newValue,
|
||||
classValidation,
|
||||
fireChangeEvent,
|
||||
);
|
||||
if (classValue instanceof Error) {
|
||||
doLogging && console.log('invalid class validation, return');
|
||||
if (doLogging) console.log('invalid class validation, return');
|
||||
return;
|
||||
}
|
||||
|
||||
const localValidation = this.getValidator()?.call(this, classValue);
|
||||
const localValue = this.processValidation_(
|
||||
const localValue = this.processValidation(
|
||||
classValue,
|
||||
localValidation,
|
||||
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;
|
||||
}
|
||||
@@ -1108,7 +1117,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,
|
||||
@@ -1120,7 +1129,10 @@ export abstract class Field<T = any>
|
||||
if (this.isDirty_) {
|
||||
this.forceRerender();
|
||||
}
|
||||
doLogging && console.log(this.value_);
|
||||
if (doLogging) console.log(this.value_);
|
||||
} finally {
|
||||
eventUtils.setGroup(existingGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1131,7 +1143,7 @@ export abstract class Field<T = any>
|
||||
* @param fireChangeEvent Whether to fire a change event if the value changes.
|
||||
* @returns New value, or an Error object.
|
||||
*/
|
||||
private processValidation_(
|
||||
private processValidation(
|
||||
newValue: AnyDuringMigration,
|
||||
validatedValue: T | null | undefined,
|
||||
fireChangeEvent: boolean,
|
||||
@@ -1245,7 +1257,7 @@ export abstract class Field<T = any>
|
||||
(clickTarget as AnyDuringMigration).tooltip = newTip;
|
||||
} else {
|
||||
// Field has not been initialized yet.
|
||||
this.tooltip_ = newTip;
|
||||
this.tooltip = newTip;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1259,8 +1271,8 @@ export abstract class Field<T = any>
|
||||
if (clickTarget) {
|
||||
return Tooltip.getTooltipOfObject(clickTarget);
|
||||
}
|
||||
// Field has not been initialized yet. Return stashed this.tooltip_ value.
|
||||
return Tooltip.getTooltipOfObject({tooltip: this.tooltip_});
|
||||
// Field has not been initialized yet. Return stashed this.tooltip value.
|
||||
return Tooltip.getTooltipOfObject({tooltip: this.tooltip});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1366,7 +1378,7 @@ export abstract class Field<T = any>
|
||||
*/
|
||||
setCursorSvg(cursorSvg: SVGElement) {
|
||||
if (!cursorSvg) {
|
||||
this.cursorSvg_ = null;
|
||||
this.cursorSvg = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1374,7 +1386,7 @@ export abstract class Field<T = any>
|
||||
throw new Error(`The field group is ${this.fieldGroup_}.`);
|
||||
}
|
||||
this.fieldGroup_.appendChild(cursorSvg);
|
||||
this.cursorSvg_ = cursorSvg;
|
||||
this.cursorSvg = cursorSvg;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1385,7 +1397,7 @@ export abstract class Field<T = any>
|
||||
*/
|
||||
setMarkerSvg(markerSvg: SVGElement) {
|
||||
if (!markerSvg) {
|
||||
this.markerSvg_ = null;
|
||||
this.markerSvg = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1393,7 +1405,7 @@ export abstract class Field<T = any>
|
||||
throw new Error(`The field group is ${this.fieldGroup_}.`);
|
||||
}
|
||||
this.fieldGroup_.appendChild(markerSvg);
|
||||
this.markerSvg_ = markerSvg;
|
||||
this.markerSvg = markerSvg;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1407,10 +1419,10 @@ export abstract class Field<T = any>
|
||||
throw new UnattachedFieldError();
|
||||
}
|
||||
const workspace = block.workspace as WorkspaceSvg;
|
||||
if (workspace.keyboardAccessibilityMode && this.cursorSvg_) {
|
||||
if (workspace.keyboardAccessibilityMode && this.cursorSvg) {
|
||||
workspace.getCursor()!.draw();
|
||||
}
|
||||
if (workspace.keyboardAccessibilityMode && this.markerSvg_) {
|
||||
if (workspace.keyboardAccessibilityMode && this.markerSvg) {
|
||||
// TODO(#4592): Update all markers on the field.
|
||||
workspace.getMarker(MarkerManager.LOCAL_MARKER)!.draw();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -165,7 +165,7 @@ export class FieldCheckbox extends Field<CheckboxBool> {
|
||||
* that this is a either 'TRUE' or 'FALSE'.
|
||||
*/
|
||||
protected override doValueUpdate_(newValue: BoolString) {
|
||||
this.value_ = this.convertValueToBool_(newValue);
|
||||
this.value_ = this.convertValueToBool(newValue);
|
||||
// Update visual.
|
||||
if (this.textElement_) {
|
||||
this.textElement_.style.display = this.value_ ? 'block' : 'none';
|
||||
@@ -196,7 +196,7 @@ export class FieldCheckbox extends Field<CheckboxBool> {
|
||||
* @returns Text representing the value of this field ('true' or 'false').
|
||||
*/
|
||||
override getText(): string {
|
||||
return String(this.convertValueToBool_(this.value_));
|
||||
return String(this.convertValueToBool(this.value_));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +208,7 @@ export class FieldCheckbox extends Field<CheckboxBool> {
|
||||
* @param value The value to convert.
|
||||
* @returns The converted value.
|
||||
*/
|
||||
private convertValueToBool_(value: CheckboxBool | null): boolean {
|
||||
private convertValueToBool(value: CheckboxBool | null): boolean {
|
||||
if (typeof value === 'string') return value === 'TRUE';
|
||||
return !!value;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
@@ -92,6 +92,15 @@ export class FieldDropdown extends Field<string> {
|
||||
private selectedOption!: MenuOption;
|
||||
override clickTarget_: SVGElement | null = null;
|
||||
|
||||
/**
|
||||
* The y offset from the top of the field to the top of the image, if an image
|
||||
* is selected.
|
||||
*/
|
||||
protected static IMAGE_Y_OFFSET = 5;
|
||||
|
||||
/** The total vertical padding above and below an image. */
|
||||
protected static IMAGE_Y_PADDING = FieldDropdown.IMAGE_Y_OFFSET * 2;
|
||||
|
||||
/**
|
||||
* @param menuGenerator A non-empty array of options for a dropdown list, or a
|
||||
* function which generates these options. Also accepts Field.SKIP_SETUP
|
||||
@@ -125,8 +134,8 @@ export class FieldDropdown extends Field<string> {
|
||||
if (menuGenerator === Field.SKIP_SETUP) return;
|
||||
|
||||
if (Array.isArray(menuGenerator)) {
|
||||
validateOptions(menuGenerator);
|
||||
const trimmed = trimOptions(menuGenerator);
|
||||
this.validateOptions(menuGenerator);
|
||||
const trimmed = this.trimOptions(menuGenerator);
|
||||
this.menuGenerator_ = trimmed.options;
|
||||
this.prefixField = trimmed.prefix || null;
|
||||
this.suffixField = trimmed.suffix || null;
|
||||
@@ -403,7 +412,7 @@ export class FieldDropdown extends Field<string> {
|
||||
if (useCache && this.generatedOptions) return this.generatedOptions;
|
||||
|
||||
this.generatedOptions = this.menuGenerator_();
|
||||
validateOptions(this.generatedOptions);
|
||||
this.validateOptions(this.generatedOptions);
|
||||
return this.generatedOptions;
|
||||
}
|
||||
|
||||
@@ -522,7 +531,7 @@ export class FieldDropdown extends Field<string> {
|
||||
const hasBorder = !!this.borderRect_;
|
||||
const height = Math.max(
|
||||
hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0,
|
||||
imageHeight + IMAGE_Y_PADDING,
|
||||
imageHeight + FieldDropdown.IMAGE_Y_PADDING,
|
||||
);
|
||||
const xPadding = hasBorder
|
||||
? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING
|
||||
@@ -653,6 +662,127 @@ export class FieldDropdown extends Field<string> {
|
||||
// override the static fromJson method.
|
||||
return new this(options.options, undefined, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factor out common words in statically defined options.
|
||||
* Create prefix and/or suffix labels.
|
||||
*/
|
||||
protected trimOptions(options: MenuOption[]): {
|
||||
options: MenuOption[];
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
} {
|
||||
let hasImages = false;
|
||||
const trimmedOptions = options.map(([label, value]): MenuOption => {
|
||||
if (typeof label === 'string') {
|
||||
return [parsing.replaceMessageReferences(label), value];
|
||||
}
|
||||
|
||||
hasImages = true;
|
||||
// Copy the image properties so they're not influenced by the original.
|
||||
// NOTE: No need to deep copy since image properties are only 1 level deep.
|
||||
const imageLabel =
|
||||
label.alt !== null
|
||||
? {...label, alt: parsing.replaceMessageReferences(label.alt)}
|
||||
: {...label};
|
||||
return [imageLabel, value];
|
||||
});
|
||||
|
||||
if (hasImages || options.length < 2) return {options: trimmedOptions};
|
||||
|
||||
const stringOptions = trimmedOptions as [string, string][];
|
||||
const stringLabels = stringOptions.map(([label]) => label);
|
||||
|
||||
const shortest = utilsString.shortestStringLength(stringLabels);
|
||||
const prefixLength = utilsString.commonWordPrefix(stringLabels, shortest);
|
||||
const suffixLength = utilsString.commonWordSuffix(stringLabels, shortest);
|
||||
|
||||
if (
|
||||
(!prefixLength && !suffixLength) ||
|
||||
shortest <= prefixLength + suffixLength
|
||||
) {
|
||||
// One or more strings will entirely vanish if we proceed. Abort.
|
||||
return {options: stringOptions};
|
||||
}
|
||||
|
||||
const prefix = prefixLength
|
||||
? stringLabels[0].substring(0, prefixLength - 1)
|
||||
: undefined;
|
||||
const suffix = suffixLength
|
||||
? stringLabels[0].substr(1 - suffixLength)
|
||||
: undefined;
|
||||
return {
|
||||
options: this.applyTrim(stringOptions, prefixLength, suffixLength),
|
||||
prefix,
|
||||
suffix,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the calculated prefix and suffix lengths to trim all of the options in
|
||||
* the given array.
|
||||
*
|
||||
* @param options Array of option tuples:
|
||||
* (human-readable text or image, language-neutral name).
|
||||
* @param prefixLength The length of the common prefix.
|
||||
* @param suffixLength The length of the common suffix
|
||||
* @returns A new array with all of the option text trimmed.
|
||||
*/
|
||||
private applyTrim(
|
||||
options: [string, string][],
|
||||
prefixLength: number,
|
||||
suffixLength: number,
|
||||
): MenuOption[] {
|
||||
return options.map(([text, value]) => [
|
||||
text.substring(prefixLength, text.length - suffixLength),
|
||||
value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data structure to be processed as an options list.
|
||||
*
|
||||
* @param options The proposed dropdown options.
|
||||
* @throws {TypeError} If proposed options are incorrectly structured.
|
||||
*/
|
||||
protected validateOptions(options: MenuOption[]) {
|
||||
if (!Array.isArray(options)) {
|
||||
throw TypeError('FieldDropdown options must be an array.');
|
||||
}
|
||||
if (!options.length) {
|
||||
throw TypeError('FieldDropdown options must not be an empty array.');
|
||||
}
|
||||
let foundError = false;
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const tuple = options[i];
|
||||
if (!Array.isArray(tuple)) {
|
||||
foundError = true;
|
||||
console.error(
|
||||
`Invalid option[${i}]: Each FieldDropdown option must be an array.
|
||||
Found: ${tuple}`,
|
||||
);
|
||||
} else if (typeof tuple[1] !== 'string') {
|
||||
foundError = true;
|
||||
console.error(
|
||||
`Invalid option[${i}]: Each FieldDropdown option id must be a string.
|
||||
Found ${tuple[1]} in: ${tuple}`,
|
||||
);
|
||||
} else if (
|
||||
tuple[0] &&
|
||||
typeof tuple[0] !== 'string' &&
|
||||
typeof tuple[0].src !== 'string'
|
||||
) {
|
||||
foundError = true;
|
||||
console.error(
|
||||
`Invalid option[${i}]: Each FieldDropdown option must have a string
|
||||
label or image description. Found ${tuple[0]} in: ${tuple}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (foundError) {
|
||||
throw TypeError('Found invalid FieldDropdown options.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,147 +843,4 @@ export interface FieldDropdownFromJsonConfig extends FieldDropdownConfig {
|
||||
*/
|
||||
export type FieldDropdownValidator = FieldValidator<string>;
|
||||
|
||||
/**
|
||||
* The y offset from the top of the field to the top of the image, if an image
|
||||
* is selected.
|
||||
*/
|
||||
const IMAGE_Y_OFFSET = 5;
|
||||
|
||||
/** The total vertical padding above and below an image. */
|
||||
const IMAGE_Y_PADDING: number = IMAGE_Y_OFFSET * 2;
|
||||
|
||||
/**
|
||||
* Factor out common words in statically defined options.
|
||||
* Create prefix and/or suffix labels.
|
||||
*/
|
||||
function trimOptions(options: MenuOption[]): {
|
||||
options: MenuOption[];
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
} {
|
||||
let hasImages = false;
|
||||
const trimmedOptions = options.map(([label, value]): MenuOption => {
|
||||
if (typeof label === 'string') {
|
||||
return [parsing.replaceMessageReferences(label), value];
|
||||
}
|
||||
|
||||
hasImages = true;
|
||||
// Copy the image properties so they're not influenced by the original.
|
||||
// NOTE: No need to deep copy since image properties are only 1 level deep.
|
||||
const imageLabel =
|
||||
label.alt !== null
|
||||
? {...label, alt: parsing.replaceMessageReferences(label.alt)}
|
||||
: {...label};
|
||||
return [imageLabel, value];
|
||||
});
|
||||
|
||||
if (hasImages || options.length < 2) return {options: trimmedOptions};
|
||||
|
||||
const stringOptions = trimmedOptions as [string, string][];
|
||||
const stringLabels = stringOptions.map(([label]) => label);
|
||||
|
||||
const shortest = utilsString.shortestStringLength(stringLabels);
|
||||
const prefixLength = utilsString.commonWordPrefix(stringLabels, shortest);
|
||||
const suffixLength = utilsString.commonWordSuffix(stringLabels, shortest);
|
||||
|
||||
if (
|
||||
(!prefixLength && !suffixLength) ||
|
||||
shortest <= prefixLength + suffixLength
|
||||
) {
|
||||
// One or more strings will entirely vanish if we proceed. Abort.
|
||||
return {options: stringOptions};
|
||||
}
|
||||
|
||||
const prefix = prefixLength
|
||||
? stringLabels[0].substring(0, prefixLength - 1)
|
||||
: undefined;
|
||||
const suffix = suffixLength
|
||||
? stringLabels[0].substr(1 - suffixLength)
|
||||
: undefined;
|
||||
return {
|
||||
options: applyTrim(stringOptions, prefixLength, suffixLength),
|
||||
prefix,
|
||||
suffix,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the calculated prefix and suffix lengths to trim all of the options in
|
||||
* the given array.
|
||||
*
|
||||
* @param options Array of option tuples:
|
||||
* (human-readable text or image, language-neutral name).
|
||||
* @param prefixLength The length of the common prefix.
|
||||
* @param suffixLength The length of the common suffix
|
||||
* @returns A new array with all of the option text trimmed.
|
||||
*/
|
||||
function applyTrim(
|
||||
options: [string, string][],
|
||||
prefixLength: number,
|
||||
suffixLength: number,
|
||||
): MenuOption[] {
|
||||
return options.map(([text, value]) => [
|
||||
text.substring(prefixLength, text.length - suffixLength),
|
||||
value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data structure to be processed as an options list.
|
||||
*
|
||||
* @param options The proposed dropdown options.
|
||||
* @throws {TypeError} If proposed options are incorrectly structured.
|
||||
*/
|
||||
function validateOptions(options: MenuOption[]) {
|
||||
if (!Array.isArray(options)) {
|
||||
throw TypeError('FieldDropdown options must be an array.');
|
||||
}
|
||||
if (!options.length) {
|
||||
throw TypeError('FieldDropdown options must not be an empty array.');
|
||||
}
|
||||
let foundError = false;
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const tuple = options[i];
|
||||
if (!Array.isArray(tuple)) {
|
||||
foundError = true;
|
||||
console.error(
|
||||
'Invalid option[' +
|
||||
i +
|
||||
']: Each FieldDropdown option must be an ' +
|
||||
'array. Found: ',
|
||||
tuple,
|
||||
);
|
||||
} else if (typeof tuple[1] !== 'string') {
|
||||
foundError = true;
|
||||
console.error(
|
||||
'Invalid option[' +
|
||||
i +
|
||||
']: Each FieldDropdown option id must be ' +
|
||||
'a string. Found ' +
|
||||
tuple[1] +
|
||||
' in: ',
|
||||
tuple,
|
||||
);
|
||||
} else if (
|
||||
tuple[0] &&
|
||||
typeof tuple[0] !== 'string' &&
|
||||
typeof tuple[0].src !== 'string'
|
||||
) {
|
||||
foundError = true;
|
||||
console.error(
|
||||
'Invalid option[' +
|
||||
i +
|
||||
']: Each FieldDropdown option must have a ' +
|
||||
'string label or image description. Found' +
|
||||
tuple[0] +
|
||||
' in: ',
|
||||
tuple,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (foundError) {
|
||||
throw TypeError('Found invalid FieldDropdown options.');
|
||||
}
|
||||
}
|
||||
|
||||
fieldRegistry.register('field_dropdown', FieldDropdown);
|
||||
|
||||
@@ -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,
|
||||
@@ -28,12 +28,13 @@ import {
|
||||
UnattachedFieldError,
|
||||
} from './field.js';
|
||||
import {Msg} from './msg.js';
|
||||
import * as renderManagement from './render_management.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.
|
||||
@@ -79,10 +80,10 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
protected valueWhenEditorWasOpened_: string | T | null = null;
|
||||
|
||||
/** Key down event data. */
|
||||
private onKeyDownWrapper_: browserEvents.Data | null = null;
|
||||
private onKeyDownWrapper: browserEvents.Data | null = null;
|
||||
|
||||
/** Key input event data. */
|
||||
private onKeyInputWrapper_: browserEvents.Data | null = null;
|
||||
private onKeyInputWrapper: browserEvents.Data | null = null;
|
||||
|
||||
/**
|
||||
* Whether the field should consider the whole parent block to be its click
|
||||
@@ -188,7 +189,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,
|
||||
@@ -338,9 +339,9 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
this.workspace_.options.modalInputs &&
|
||||
(userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD)
|
||||
) {
|
||||
this.showPromptEditor_();
|
||||
this.showPromptEditor();
|
||||
} else {
|
||||
this.showInlineEditor_(quietInput);
|
||||
this.showInlineEditor(quietInput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +350,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
* Mobile browsers may have issues with in-line textareas (focus and
|
||||
* keyboards).
|
||||
*/
|
||||
private showPromptEditor_() {
|
||||
private showPromptEditor() {
|
||||
dialog.prompt(
|
||||
Msg['CHANGE_VALUE_TITLE'],
|
||||
this.getText(),
|
||||
@@ -368,7 +369,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
*
|
||||
* @param quietInput True if editor should be created without focus.
|
||||
*/
|
||||
private showInlineEditor_(quietInput: boolean) {
|
||||
private showInlineEditor(quietInput: boolean) {
|
||||
const block = this.getSourceBlock();
|
||||
if (!block) {
|
||||
throw new UnattachedFieldError();
|
||||
@@ -476,7 +477,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,
|
||||
@@ -518,30 +519,30 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
*/
|
||||
protected bindInputEvents_(htmlInput: HTMLElement) {
|
||||
// Trap Enter without IME and Esc to hide.
|
||||
this.onKeyDownWrapper_ = browserEvents.conditionalBind(
|
||||
this.onKeyDownWrapper = browserEvents.conditionalBind(
|
||||
htmlInput,
|
||||
'keydown',
|
||||
this,
|
||||
this.onHtmlInputKeyDown_,
|
||||
);
|
||||
// Resize after every input change.
|
||||
this.onKeyInputWrapper_ = browserEvents.conditionalBind(
|
||||
this.onKeyInputWrapper = browserEvents.conditionalBind(
|
||||
htmlInput,
|
||||
'input',
|
||||
this,
|
||||
this.onHtmlInputChange_,
|
||||
this.onHtmlInputChange,
|
||||
);
|
||||
}
|
||||
|
||||
/** Unbind handlers for user input and workspace size changes. */
|
||||
protected unbindInputEvents_() {
|
||||
if (this.onKeyDownWrapper_) {
|
||||
browserEvents.unbind(this.onKeyDownWrapper_);
|
||||
this.onKeyDownWrapper_ = null;
|
||||
if (this.onKeyDownWrapper) {
|
||||
browserEvents.unbind(this.onKeyDownWrapper);
|
||||
this.onKeyDownWrapper = null;
|
||||
}
|
||||
if (this.onKeyInputWrapper_) {
|
||||
browserEvents.unbind(this.onKeyInputWrapper_);
|
||||
this.onKeyInputWrapper_ = null;
|
||||
if (this.onKeyInputWrapper) {
|
||||
browserEvents.unbind(this.onKeyInputWrapper);
|
||||
this.onKeyInputWrapper = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +575,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
*
|
||||
* @param _e Keyboard event.
|
||||
*/
|
||||
private onHtmlInputChange_(_e: Event) {
|
||||
private onHtmlInputChange(_e: Event) {
|
||||
// Intermediate value changes from user input are not confirmed until the
|
||||
// user closes the editor, and may be numerous. Inhibit reporting these as
|
||||
// normal block change events, and instead report them as special
|
||||
@@ -593,7 +594,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,
|
||||
@@ -630,10 +631,9 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
|
||||
/** Resize the editor to fit the text. */
|
||||
protected resizeEditor_() {
|
||||
renderManagement.finishQueuedRenders().then(() => {
|
||||
const block = this.getSourceBlock();
|
||||
if (!block) {
|
||||
throw new UnattachedFieldError();
|
||||
}
|
||||
if (!block) throw new UnattachedFieldError();
|
||||
const div = WidgetDiv.getDiv();
|
||||
const bBox = this.getScaledBBox();
|
||||
div!.style.width = bBox.right - bBox.left + 'px';
|
||||
@@ -642,10 +642,11 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
// In RTL mode block fields and LTR input fields the left edge moves,
|
||||
// whereas the right edge is fixed. Reposition the editor.
|
||||
const x = block.RTL ? bBox.right - div!.offsetWidth : bBox.left;
|
||||
const xy = new Coordinate(x, bBox.top);
|
||||
const y = bBox.top;
|
||||
|
||||
div!.style.left = xy.x + 'px';
|
||||
div!.style.top = xy.y + 'px';
|
||||
div!.style.left = `${x}px`;
|
||||
div!.style.top = `${y}px`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -657,7 +658,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
|
||||
* div.
|
||||
*/
|
||||
override repositionForWindowResize(): boolean {
|
||||
const block = this.getSourceBlock();
|
||||
const block = this.getSourceBlock()?.getRootBlock();
|
||||
// This shouldn't be possible. We should never have a WidgetDiv if not using
|
||||
// rendered blocks.
|
||||
if (!(block instanceof BlockSvg)) return false;
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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';
|
||||
import * as dom from './utils/dom.js';
|
||||
|
||||
|
||||
@@ -11,17 +11,22 @@
|
||||
*/
|
||||
// Former goog.module ID: Blockly.Flyout
|
||||
|
||||
import type {Abstract as AbstractEvent} from './events/events_abstract.js';
|
||||
import {BlockSvg} from './block_svg.js';
|
||||
import * as browserEvents from './browser_events.js';
|
||||
import {ComponentManager} from './component_manager.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 {FlyoutMetricsManager} from './flyout_metrics_manager.js';
|
||||
import {FlyoutSeparator, SeparatorAxis} from './flyout_separator.js';
|
||||
import {IAutoHideable} from './interfaces/i_autohideable.js';
|
||||
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
|
||||
import type {IFlyout} from './interfaces/i_flyout.js';
|
||||
import type {IFlyoutInflater} from './interfaces/i_flyout_inflater.js';
|
||||
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
|
||||
import type {Options} from './options.js';
|
||||
import * as registry from './registry.js';
|
||||
import * as renderManagement from './render_management.js';
|
||||
import {ScrollbarPair} from './scrollbar_pair.js';
|
||||
import * as blocks from './serialization/blocks.js';
|
||||
import {Coordinate} from './utils/coordinate.js';
|
||||
@@ -31,10 +36,6 @@ import {Svg} from './utils/svg.js';
|
||||
import * as toolbox from './utils/toolbox.js';
|
||||
import * as Variables from './variables.js';
|
||||
import {WorkspaceSvg} from './workspace_svg.js';
|
||||
import * as registry from './registry.js';
|
||||
import * as renderManagement from './render_management.js';
|
||||
import {IAutoHideable} from './interfaces/i_autohideable.js';
|
||||
import {FlyoutSeparator, SeparatorAxis} from './flyout_separator.js';
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
@@ -152,7 +153,7 @@ export abstract class Flyout
|
||||
/**
|
||||
* Whether the flyout is visible.
|
||||
*/
|
||||
private isVisible_ = false;
|
||||
private visible = false;
|
||||
|
||||
/**
|
||||
* Whether the workspace containing this flyout is visible.
|
||||
@@ -237,7 +238,7 @@ export abstract class Flyout
|
||||
|
||||
this.workspace_.internalIsFlyout = true;
|
||||
// Keep the workspace visibility consistent with the flyout's visibility.
|
||||
this.workspace_.setVisible(this.isVisible_);
|
||||
this.workspace_.setVisible(this.visible);
|
||||
|
||||
/**
|
||||
* The unique id for this component that is used to register with the
|
||||
@@ -369,7 +370,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,
|
||||
@@ -470,7 +471,7 @@ export abstract class Flyout
|
||||
* @returns True if visible.
|
||||
*/
|
||||
isVisible(): boolean {
|
||||
return this.isVisible_;
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,7 +484,7 @@ export abstract class Flyout
|
||||
setVisible(visible: boolean) {
|
||||
const visibilityChanged = visible !== this.isVisible();
|
||||
|
||||
this.isVisible_ = visible;
|
||||
this.visible = visible;
|
||||
if (visibilityChanged) {
|
||||
if (!this.autoClose) {
|
||||
// Auto-close flyouts are ignored as drag targets, so only non
|
||||
@@ -818,13 +819,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();
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
*/
|
||||
// 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 type {IBoundedElement} from './interfaces/i_bounded_element.js';
|
||||
import type {IRenderedElement} from './interfaces/i_rendered_element.js';
|
||||
import {Coordinate} from './utils/coordinate.js';
|
||||
import * as dom from './utils/dom.js';
|
||||
import * as parsing from './utils/parsing.js';
|
||||
import {Rect} from './utils/rect.js';
|
||||
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 './interfaces/i_ast_node_location_svg.js';
|
||||
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
|
||||
import type {IRenderedElement} from './interfaces/i_rendered_element.js';
|
||||
import {Rect} from './utils/rect.js';
|
||||
|
||||
/**
|
||||
* Class for a button or label in the flyout.
|
||||
|
||||
@@ -38,13 +38,13 @@ export class FlyoutMetricsManager extends MetricsManager {
|
||||
*
|
||||
* @returns The bounding box of the blocks on the workspace.
|
||||
*/
|
||||
private getBoundingBox_():
|
||||
private getBoundingBox():
|
||||
| SVGRect
|
||||
| {height: number; y: number; width: number; x: number} {
|
||||
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.
|
||||
@@ -55,7 +55,7 @@ export class FlyoutMetricsManager extends MetricsManager {
|
||||
|
||||
override getContentMetrics(opt_getWorkspaceCoordinates?: boolean) {
|
||||
// The bounding box is in workspace coordinates.
|
||||
const blockBoundingBox = this.getBoundingBox_();
|
||||
const blockBoundingBox = this.getBoundingBox();
|
||||
const scale = opt_getWorkspaceCoordinates ? 1 : this.workspace_.scale;
|
||||
|
||||
return {
|
||||
|
||||
@@ -24,7 +24,7 @@ import type {Workspace} from './workspace.js';
|
||||
* @deprecated
|
||||
* @see {@link https://developers.google.com/blockly/guides/create-custom-blocks/generating-code}
|
||||
* @param block The Block instance to generate code for.
|
||||
* @param genearator The CodeGenerator calling the function.
|
||||
* @param generator The CodeGenerator calling the function.
|
||||
* @returns A string containing the generated code (for statement blocks),
|
||||
* or a [code, precedence] tuple (for value/expression blocks), or
|
||||
* null if no code should be emitted for block.
|
||||
|
||||
@@ -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
|
||||
@@ -145,7 +146,7 @@ export class Gesture {
|
||||
private mostRecentEvent: PointerEvent;
|
||||
|
||||
/** Boolean for whether or not this gesture is a multi-touch gesture. */
|
||||
private isMultiTouch_ = false;
|
||||
private multiTouch = false;
|
||||
|
||||
/** A map of cached points used for tracking multi-touch gestures. */
|
||||
private cachedPoints = new Map<string, Coordinate | null>();
|
||||
@@ -585,7 +586,7 @@ export class Gesture {
|
||||
const point0 = this.cachedPoints.get(pointers[0])!;
|
||||
const point1 = this.cachedPoints.get(pointers[1])!;
|
||||
this.startDistance = Coordinate.distance(point0, point1);
|
||||
this.isMultiTouch_ = true;
|
||||
this.multiTouch = true;
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +691,7 @@ export class Gesture {
|
||||
* @internal
|
||||
*/
|
||||
isMultiTouch(): boolean {
|
||||
return this.isMultiTouch_;
|
||||
return this.multiTouch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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',
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -8,21 +8,21 @@
|
||||
|
||||
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 {TextInputBubble} from '../bubbles/textinput_bubble.js';
|
||||
import {EventType} from '../events/type.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 * 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 * 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 * as renderManagement from '../render_management.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;
|
||||
@@ -46,12 +46,9 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
*/
|
||||
static readonly WEIGHT = 3;
|
||||
|
||||
/** The bubble used to show editable text to the user. */
|
||||
/** The bubble used to show comment text to the user. */
|
||||
private textInputBubble: TextInputBubble | null = null;
|
||||
|
||||
/** The bubble used to show non-editable text to the user. */
|
||||
private textBubble: TextBubble | null = null;
|
||||
|
||||
/** The text of this comment. */
|
||||
private text = '';
|
||||
|
||||
@@ -120,7 +117,6 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
this.textInputBubble?.dispose();
|
||||
this.textBubble?.dispose();
|
||||
}
|
||||
|
||||
override getWeight(): number {
|
||||
@@ -135,7 +131,6 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
super.applyColour();
|
||||
const colour = (this.sourceBlock as BlockSvg).style.colourPrimary;
|
||||
this.textInputBubble?.setColour(colour);
|
||||
this.textBubble?.setColour(colour);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,14 +156,13 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
}
|
||||
const anchorLocation = this.getAnchorLocation();
|
||||
this.textInputBubble?.setAnchorLocation(anchorLocation);
|
||||
this.textBubble?.setAnchorLocation(anchorLocation);
|
||||
}
|
||||
|
||||
/** Sets the text of this comment. Updates any bubbles if they are visible. */
|
||||
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,
|
||||
@@ -178,7 +172,6 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
);
|
||||
this.text = text;
|
||||
this.textInputBubble?.setText(this.text);
|
||||
this.textBubble?.setText(this.text);
|
||||
}
|
||||
|
||||
/** Returns the text of this comment. */
|
||||
@@ -282,7 +275,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,
|
||||
@@ -338,7 +331,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',
|
||||
@@ -351,6 +344,18 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
* to update the state of this icon in response to changes in the bubble.
|
||||
*/
|
||||
private showEditableBubble() {
|
||||
this.createBubble();
|
||||
this.textInputBubble?.addTextChangeListener(() => this.onTextChange());
|
||||
this.textInputBubble?.addSizeChangeListener(() => this.onSizeChange());
|
||||
}
|
||||
|
||||
/** Shows the non editable text bubble for this comment. */
|
||||
private showNonEditableBubble() {
|
||||
this.createBubble();
|
||||
this.textInputBubble?.setEditable(false);
|
||||
}
|
||||
|
||||
protected createBubble() {
|
||||
this.textInputBubble = new TextInputBubble(
|
||||
this.sourceBlock.workspace as WorkspaceSvg,
|
||||
this.getAnchorLocation(),
|
||||
@@ -368,25 +373,10 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
);
|
||||
}
|
||||
|
||||
/** Shows the non editable text bubble for this comment. */
|
||||
private showNonEditableBubble() {
|
||||
this.textBubble = new TextBubble(
|
||||
this.getText(),
|
||||
this.sourceBlock.workspace as WorkspaceSvg,
|
||||
this.getAnchorLocation(),
|
||||
this.getBubbleOwnerRect(),
|
||||
);
|
||||
if (this.bubbleLocation) {
|
||||
this.textBubble.moveDuringDrag(this.bubbleLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hides any open bubbles owned by this comment. */
|
||||
private hideBubble() {
|
||||
this.textInputBubble?.dispose();
|
||||
this.textInputBubble = null;
|
||||
this.textBubble?.dispose();
|
||||
this.textBubble = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,8 +396,7 @@ export class CommentIcon extends Icon implements IHasBubble, ISerializable {
|
||||
* I.E. the block that owns this icon.
|
||||
*/
|
||||
private getBubbleOwnerRect(): Rect {
|
||||
const bbox = (this.sourceBlock as BlockSvg).getSvgRoot().getBBox();
|
||||
return new Rect(bbox.y, bbox.y + bbox.height, bbox.x, bbox.x + bbox.width);
|
||||
return (this.sourceBlock as BlockSvg).getBoundingRectangleWithoutChildren();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -22,7 +22,6 @@ import * as Touch from './touch.js';
|
||||
import * as aria from './utils/aria.js';
|
||||
import * as dom from './utils/dom.js';
|
||||
import {Svg} from './utils/svg.js';
|
||||
import * as userAgent from './utils/useragent.js';
|
||||
import * as WidgetDiv from './widgetdiv.js';
|
||||
import {WorkspaceSvg} from './workspace_svg.js';
|
||||
|
||||
@@ -342,18 +341,6 @@ function bindDocumentEvents() {
|
||||
// should run regardless of what other touch event handlers have run.
|
||||
browserEvents.bind(document, 'touchend', null, Touch.longStop);
|
||||
browserEvents.bind(document, 'touchcancel', null, Touch.longStop);
|
||||
// Some iPad versions don't fire resize after portrait to landscape change.
|
||||
if (userAgent.IPAD) {
|
||||
browserEvents.conditionalBind(
|
||||
window,
|
||||
'orientationchange',
|
||||
document,
|
||||
function () {
|
||||
// TODO (#397): Fix for multiple Blockly workspaces.
|
||||
common.svgResize(common.getMainWorkspace() as WorkspaceSvg);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
documentEventsBound = true;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user