Files
blockly/core/block_drag_surface.ts
Maribeth Bottorff ec72e8bb4d chore: add api extractor configuration and fix some associated problems (#6388)
* chore: add configuration for api extractor

* fix: remove extra param names

* chore: private to internal

* remove unrestricted

* chore: remove double backticks

* chore: remove fileoverview and export * as

* chore: return to returns

* chore: fix backslashes and angle brackets in tsdoc

* chore: final to sealed

* chore: ignore to internal

* chore: fix link tags

* chore: add api-extractor configuration

* chore: add unrecognized tag names

* chore: remove tsdoc-metadata

* fix: correct index.d.ts

* chore: fix connection link
2022-09-01 11:39:05 -07:00

242 lines
7.7 KiB
TypeScript

/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* 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 deprecation from './utils/deprecation.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;
/**
* This is where blocks live while they are being dragged if the drag
* surface is enabled.
*/
private dragGroup_: SVGElement;
/**
* 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 = new Coordinate(0, 0);
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.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);
this.dragGroup_ = dom.createSvgElement(Svg.G, {}, this.svg_ as SVGElement);
}
/** Create the drag surface and inject it into the container. */
createDom() {
// No alternative provided, because now the dom is just automatically
// created in the constructor now.
deprecation.warn('BlockDragSurfaceSvg createDom', 'June 2022', 'June 2023');
}
/**
* 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.
*
* @internal
*/
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';
dom.setCssTransform(this.svg_, '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.
*
* @returns 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).
*
* @returns 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).
*
* @returns 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.
*
* @returns 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_ = new Coordinate(0, 0);
}
}