mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
* chore: add and configure prettier * chore: remove clang-format * chore: remove clang-format config * chore: lint additional ts files * chore: fix lint errors in blocks * chore: add prettier-ignore where needed * chore: ignore js blocks when formatting * chore: fix playground html syntax * chore: fix yaml spacing from merge * chore: convert text blocks to use arrow functions * chore: format everything with prettier * chore: fix lint unused imports in blocks
193 lines
6.0 KiB
TypeScript
193 lines
6.0 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2021 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import * as goog from '../closure/goog/goog.js';
|
|
goog.declareModuleId('Blockly.bumpObjects');
|
|
|
|
import type {BlockSvg} from './block_svg.js';
|
|
import type {Abstract} from './events/events_abstract.js';
|
|
import type {BlockCreate} from './events/events_block_create.js';
|
|
import type {BlockMove} from './events/events_block_move.js';
|
|
import type {CommentCreate} from './events/events_comment_create.js';
|
|
import type {CommentMove} from './events/events_comment_move.js';
|
|
import type {ViewportChange} from './events/events_viewport.js';
|
|
import * as eventUtils from './events/utils.js';
|
|
import type {IBoundedElement} from './interfaces/i_bounded_element.js';
|
|
import type {ContainerRegion} from './metrics_manager.js';
|
|
import * as mathUtils from './utils/math.js';
|
|
import type {WorkspaceCommentSvg} from './workspace_comment_svg.js';
|
|
import type {WorkspaceSvg} from './workspace_svg.js';
|
|
|
|
/**
|
|
* Bumps the given object that has passed out of bounds.
|
|
*
|
|
* @param workspace The workspace containing the object.
|
|
* @param bounds The region to bump an object into. For example, pass
|
|
* ScrollMetrics to bump a block into the scrollable region of the
|
|
* workspace, or pass ViewMetrics to bump a block into the visible region of
|
|
* the workspace. This should be specified in workspace coordinates.
|
|
* @param object The object to bump.
|
|
* @returns True if object was bumped.
|
|
*/
|
|
function bumpObjectIntoBounds(
|
|
workspace: WorkspaceSvg,
|
|
bounds: ContainerRegion,
|
|
object: IBoundedElement
|
|
): boolean {
|
|
// Compute new top/left position for object.
|
|
const objectMetrics = object.getBoundingRectangle();
|
|
const height = objectMetrics.bottom - objectMetrics.top;
|
|
const width = objectMetrics.right - objectMetrics.left;
|
|
|
|
const topClamp = bounds.top;
|
|
const boundsBottom = bounds.top + bounds.height;
|
|
const bottomClamp = boundsBottom - height;
|
|
// If the object is taller than the workspace we want to
|
|
// top-align the block
|
|
const newYPosition = mathUtils.clamp(
|
|
topClamp,
|
|
objectMetrics.top,
|
|
bottomClamp
|
|
);
|
|
const deltaY = newYPosition - objectMetrics.top;
|
|
|
|
// Note: Even in RTL mode the "anchor" of the object is the
|
|
// top-left corner of the object.
|
|
let leftClamp = bounds.left;
|
|
const boundsRight = bounds.left + bounds.width;
|
|
let rightClamp = boundsRight - width;
|
|
if (workspace.RTL) {
|
|
// If the object is wider than the workspace and we're in RTL
|
|
// mode we want to right-align the block, which means setting
|
|
// the left clamp to match.
|
|
leftClamp = Math.min(rightClamp, leftClamp);
|
|
} else {
|
|
// If the object is wider than the workspace and we're in LTR
|
|
// mode we want to left-align the block, which means setting
|
|
// the right clamp to match.
|
|
rightClamp = Math.max(leftClamp, rightClamp);
|
|
}
|
|
const newXPosition = mathUtils.clamp(
|
|
leftClamp,
|
|
objectMetrics.left,
|
|
rightClamp
|
|
);
|
|
const deltaX = newXPosition - objectMetrics.left;
|
|
|
|
if (deltaX || deltaY) {
|
|
object.moveBy(deltaX, deltaY, ['inbounds']);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
export const bumpIntoBounds = bumpObjectIntoBounds;
|
|
|
|
/**
|
|
* Creates a handler for bumping objects when they cross fixed bounds.
|
|
*
|
|
* @param workspace The workspace to handle.
|
|
* @returns The event handler.
|
|
*/
|
|
export function bumpIntoBoundsHandler(
|
|
workspace: WorkspaceSvg
|
|
): (p1: Abstract) => void {
|
|
return (e) => {
|
|
const metricsManager = workspace.getMetricsManager();
|
|
if (!metricsManager.hasFixedEdges() || workspace.isDragging()) {
|
|
return;
|
|
}
|
|
|
|
if (eventUtils.BUMP_EVENTS.indexOf(e.type ?? '') !== -1) {
|
|
const scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true);
|
|
|
|
// Triggered by move/create event
|
|
const object = extractObjectFromEvent(
|
|
workspace,
|
|
e as eventUtils.BumpEvent
|
|
);
|
|
if (!object) {
|
|
return;
|
|
}
|
|
// Handle undo.
|
|
const existingGroup = eventUtils.getGroup() || false;
|
|
eventUtils.setGroup(e.group);
|
|
|
|
const wasBumped = bumpObjectIntoBounds(
|
|
workspace,
|
|
scrollMetricsInWsCoords,
|
|
object as IBoundedElement
|
|
);
|
|
|
|
if (wasBumped && !e.group) {
|
|
console.warn(
|
|
'Moved object in bounds but there was no' +
|
|
' event group. This may break undo.'
|
|
);
|
|
}
|
|
eventUtils.setGroup(existingGroup);
|
|
} else if (e.type === eventUtils.VIEWPORT_CHANGE) {
|
|
const viewportEvent = e as ViewportChange;
|
|
if (
|
|
viewportEvent.scale &&
|
|
viewportEvent.oldScale &&
|
|
viewportEvent.scale > viewportEvent.oldScale
|
|
) {
|
|
bumpTopObjectsIntoBounds(workspace);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Extracts the object from the given event.
|
|
*
|
|
* @param workspace The workspace the event originated
|
|
* from.
|
|
* @param e An event containing an object.
|
|
* @returns The extracted
|
|
* object.
|
|
*/
|
|
function extractObjectFromEvent(
|
|
workspace: WorkspaceSvg,
|
|
e: eventUtils.BumpEvent
|
|
): BlockSvg | null | WorkspaceCommentSvg {
|
|
let object = null;
|
|
switch (e.type) {
|
|
case eventUtils.BLOCK_CREATE:
|
|
case eventUtils.BLOCK_MOVE:
|
|
object = workspace.getBlockById((e as BlockCreate | BlockMove).blockId!);
|
|
if (object) {
|
|
object = object.getRootBlock();
|
|
}
|
|
break;
|
|
case eventUtils.COMMENT_CREATE:
|
|
case eventUtils.COMMENT_MOVE:
|
|
object = workspace.getCommentById(
|
|
(e as CommentCreate | CommentMove).commentId!
|
|
) as WorkspaceCommentSvg | null;
|
|
break;
|
|
}
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* Bumps the top objects in the given workspace into bounds.
|
|
*
|
|
* @param workspace The workspace.
|
|
*/
|
|
export function bumpTopObjectsIntoBounds(workspace: WorkspaceSvg) {
|
|
const metricsManager = workspace.getMetricsManager();
|
|
if (!metricsManager.hasFixedEdges() || workspace.isDragging()) {
|
|
return;
|
|
}
|
|
|
|
const scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true);
|
|
const topBlocks = workspace.getTopBoundedElements();
|
|
for (let i = 0, block; (block = topBlocks[i]); i++) {
|
|
bumpObjectIntoBounds(workspace, scrollMetricsInWsCoords, block);
|
|
}
|
|
}
|