Files
blockly/core/block_drag_surface.ts
Beka Westberg 29e1f0cb03 fix: tsc errors picked up from develop (#6224)
* fix: relative path for deprecation utils

* fix: checking if properties exist in svg_math

* fix: set all timeout PIDs to AnyDuringMigration

* fix: make nullability errors explicity in block drag surface

* fix: make null check in events_block_change explicit

* fix: make getEventWorkspace_ internal so we can access it from CommentCreateDeleteHelper

* fix: rename DIV -> containerDiv in tooltip

* fix: ignore backwards compat check in category

* fix: set block styles to AnyDuringMigration

* fix: type typo in KeyboardShortcut

* fix: constants name in row measurables

* fix: typecast in mutator

* fix: populateProcedures type of flattened array

* fix: ignore errors related to workspace comment deserialization

* chore: format files

* fix: renaming imports missing file extensions

* fix: remove check for sound.play

* fix: temporarily remove bad requireType.

All `export type` statements are stripped when tsc is run. This means
that when we attempt to require BlockDefinition from the block files, we
get an error because it does not exist.

We decided to temporarily remove the require, because this will no
longer be a problem when we conver the blocks to typescript, and
everything gets compiled together.

* fix: bad jsdoc in array

* fix: silence missing property errors

Closure was complaining about inexistant properties, but they actually
do exist, they're just not being transpiled by tsc in a way that closure
understands.

I.E. if things are initialized in a function called by the constructor,
rather than in a class field or in the custructor itself, closure would
error.

It would also error on enums, because they are transpiled to a weird
IIFE.

* fix: context menu action handler not knowing the type of this.

this: TypeX information gets stripped when tsc is run, so closure could
not know that this was not global. Fixed this by reorganizing to use the
option object directly instead of passing it to onAction to be bound to
this.

* fix: readd getDeveloperVars checks (should not be part of migration)

This was found because ALL_DEVELOPER_VARS_WARNINGS_BY_BLOCK_TYPE was no
longer being accessed.

* fix: silence closure errors about overriding supertype props

We propertly define the overrides in typescript, but these get removed
from the compiled output, so closure doesn't know they exist.

* fix: silence globalThis errors

this: TypeX annotations get stripped from the compiled output, so
closure can't know that we're accessing the correct things. However,
typescript makes sure that this always has the correct properties, so
silencing this should be fine.

* fix: bad jsdoc name

* chore: attempt compiling with blockly.js

* fix: attempt moving the import statement above the namespace line

* chore: add todo comments to block def files

* chore: remove todo from context menu

* chore: add comments abotu disabled errors
2022-06-27 09:25:56 -07:00

247 lines
8.2 KiB
TypeScript

/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview A class that manages a surface for dragging blocks. When a
* block drag is started, we move the block (and children) to a separate DOM
* element that we move around using translate3d. At the end of the drag, the
* blocks are put back in into the SVG they came from. This helps
* performance by avoiding repainting the entire SVG on every mouse move
* while dragging blocks.
*/
/**
* A class that manages a surface for dragging blocks. When a
* block drag is started, we move the block (and children) to a separate DOM
* element that we move around using translate3d. At the end of the drag, the
* blocks are put back in into the SVG they came from. This helps
* performance by avoiding repainting the entire SVG on every mouse move
* while dragging blocks.
* @class
*/
import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.BlockDragSurfaceSvg');
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
import {Svg} from './utils/svg.js';
import * as svgMath from './utils/svg_math.js';
/**
* Class for a drag surface for the currently dragged block. This is a separate
* SVG that contains only the currently moving block, or nothing.
* @alias Blockly.BlockDragSurfaceSvg
*/
export class BlockDragSurfaceSvg {
/** The SVG drag surface. Set once by BlockDragSurfaceSvg.createDom. */
private SVG_: SVGElement|null = null;
/**
* This is where blocks live while they are being dragged if the drag
* surface is enabled.
*/
private dragGroup_: SVGElement|null = null;
/**
* Cached value for the scale of the drag surface.
* Used to set/get the correct translation during and after a drag.
*/
private scale_ = 1;
/**
* Cached value for the translation of the drag surface.
* This translation is in pixel units, because the scale is applied to the
* drag group rather than the top-level SVG.
*/
private surfaceXY_: Coordinate|null = null;
private readonly childSurfaceXY_: Coordinate;
/** @param container Containing element. */
constructor(private readonly container: Element) {
/**
* Cached value for the translation of the child drag surface in pixel
* units. Since the child drag surface tracks the translation of the
* workspace this is ultimately the translation of the workspace.
*/
this.childSurfaceXY_ = new Coordinate(0, 0);
this.createDom();
}
/** Create the drag surface and inject it into the container. */
createDom() {
if (this.SVG_) {
return;
}
// Already created.
this.SVG_ = dom.createSvgElement(
Svg.SVG, {
'xmlns': dom.SVG_NS,
'xmlns:html': dom.HTML_NS,
'xmlns:xlink': dom.XLINK_NS,
'version': '1.1',
'class': 'blocklyBlockDragSurface',
},
this.container);
// AnyDuringMigration because: Argument of type 'SVGElement | null' is not
// assignable to parameter of type 'Element | undefined'.
this.dragGroup_ =
dom.createSvgElement(Svg.G, {}, this.SVG_ as AnyDuringMigration);
}
/**
* Set the SVG blocks on the drag surface's group and show the surface.
* Only one block group should be on the drag surface at a time.
* @param blocks Block or group of blocks to place on the drag surface.
*/
setBlocksAndShow(blocks: SVGElement) {
if (this.dragGroup_!.childNodes.length) {
throw Error('Already dragging a block.');
}
// appendChild removes the blocks from the previous parent
this.dragGroup_!.appendChild(blocks);
this.SVG_!.style.display = 'block';
this.surfaceXY_ = new Coordinate(0, 0);
}
/**
* Translate and scale the entire drag surface group to the given position, to
* keep in sync with the workspace.
* @param x X translation in pixel coordinates.
* @param y Y translation in pixel coordinates.
* @param scale Scale of the group.
*/
translateAndScaleGroup(x: number, y: number, scale: number) {
this.scale_ = scale;
// Make sure the svg exists on a pixel boundary so that it is not fuzzy.
const roundX = Math.round(x);
const roundY = Math.round(y);
this.childSurfaceXY_.x = roundX;
this.childSurfaceXY_.y = roundY;
this.dragGroup_!.setAttribute(
'transform',
'translate(' + roundX + ',' + roundY + ') scale(' + scale + ')');
}
/**
* Translate the drag surface's SVG based on its internal state.
* @private
*/
translateSurfaceInternal_() {
let x = this.surfaceXY_!.x;
let y = this.surfaceXY_!.y;
// Make sure the svg exists on a pixel boundary so that it is not fuzzy.
x = Math.round(x);
y = Math.round(y);
this.SVG_!.style.display = 'block';
// AnyDuringMigration because: Argument of type 'SVGElement | null' is not
// assignable to parameter of type 'Element'.
dom.setCssTransform(
this.SVG_ as AnyDuringMigration,
'translate3d(' + x + 'px, ' + y + 'px, 0)');
}
/**
* Translates the entire surface by a relative offset.
* @param deltaX Horizontal offset in pixel units.
* @param deltaY Vertical offset in pixel units.
*/
translateBy(deltaX: number, deltaY: number) {
const x = this.surfaceXY_!.x + deltaX;
const y = this.surfaceXY_!.y + deltaY;
this.surfaceXY_ = new Coordinate(x, y);
this.translateSurfaceInternal_();
}
/**
* Translate the entire drag surface during a drag.
* We translate the drag surface instead of the blocks inside the surface
* so that the browser avoids repainting the SVG.
* Because of this, the drag coordinates must be adjusted by scale.
* @param x X translation for the entire surface.
* @param y Y translation for the entire surface.
*/
translateSurface(x: number, y: number) {
this.surfaceXY_ = new Coordinate(x * this.scale_, y * this.scale_);
this.translateSurfaceInternal_();
}
/**
* Reports the surface translation in scaled workspace coordinates.
* Use this when finishing a drag to return blocks to the correct position.
* @return Current translation of the surface.
*/
getSurfaceTranslation(): Coordinate {
const xy = svgMath.getRelativeXY(this.SVG_ as SVGElement);
return new Coordinate(xy.x / this.scale_, xy.y / this.scale_);
}
/**
* Provide a reference to the drag group (primarily for
* BlockSvg.getRelativeToSurfaceXY).
* @return Drag surface group element.
*/
getGroup(): SVGElement|null {
return this.dragGroup_;
}
/**
* Returns the SVG drag surface.
* @returns The SVG drag surface.
*/
getSvgRoot(): SVGElement|null {
return this.SVG_;
}
/**
* Get the current blocks on the drag surface, if any (primarily
* for BlockSvg.getRelativeToSurfaceXY).
* @return Drag surface block DOM element, or null if no blocks exist.
*/
getCurrentBlock(): Element|null {
return this.dragGroup_!.firstChild as Element;
}
/**
* Gets the translation of the child block surface
* This surface is in charge of keeping track of how much the workspace has
* moved.
* @return The amount the workspace has been moved.
*/
getWsTranslation(): Coordinate {
// Returning a copy so the coordinate can not be changed outside this class.
return this.childSurfaceXY_.clone();
}
/**
* Clear the group and hide the surface; move the blocks off onto the provided
* element.
* If the block is being deleted it doesn't need to go back to the original
* surface, since it would be removed immediately during dispose.
* @param opt_newSurface Surface the dragging blocks should be moved to, or
* null if the blocks should be removed from this surface without being
* moved to a different surface.
*/
clearAndHide(opt_newSurface?: Element) {
const currentBlockElement = this.getCurrentBlock();
if (currentBlockElement) {
if (opt_newSurface) {
// appendChild removes the node from this.dragGroup_
opt_newSurface.appendChild(currentBlockElement);
} else {
this.dragGroup_!.removeChild(currentBlockElement);
}
}
this.SVG_!.style.display = 'none';
if (this.dragGroup_!.childNodes.length) {
throw Error('Drag group was not cleared.');
}
this.surfaceXY_ = null;
}
}