mirror of
https://github.com/google/blockly.git
synced 2025-12-15 22:00:07 +01:00
* 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
242 lines
7.7 KiB
TypeScript
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);
|
|
}
|
|
}
|