Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,435 changes: 1,204 additions & 1,231 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
"workspaces": [
"packages/*"
],
"devDependencies": {
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0"
},
"scripts": {
"test": "npm run test --ws --if-present",
"lint": "npm run lint --ws --if-present",
Expand Down
8 changes: 5 additions & 3 deletions packages/blockly/core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,8 @@ export class BlockSvg
* @returns Object with .x and .y properties in workspace coordinates.
*/
override getRelativeToSurfaceXY(): Coordinate {
const layerManger = this.workspace.getLayerManager();
if (!layerManger) {
const layerManager = this.workspace.getLayerManager();
if (!layerManager) {
throw new Error(
'Cannot calculate position because the workspace has not been appended',
);
Expand All @@ -371,7 +371,7 @@ export class BlockSvg
x += xy.x;
y += xy.y;
element = element.parentNode as SVGElement;
} while (element && !layerManger.hasLayer(element));
} while (element && !layerManager.hasLayer(element));
}
return new Coordinate(x, y);
}
Expand Down Expand Up @@ -1126,7 +1126,9 @@ export class BlockSvg
if (this.isDeadOrDying()) return;
const gesture = this.workspace.getGesture(e);
if (gesture) {
this.bringToFront();
gesture.setStartIcon(icon);
getFocusManager().focusNode(icon);
}
};
}
Expand Down
9 changes: 8 additions & 1 deletion packages/blockly/core/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ let content = `

.blocklyBlockCanvas.blocklyCanvasTransitioning,
.blocklyBubbleCanvas.blocklyCanvasTransitioning {
transition: transform .5s;
transition: transform .15s;
}

@media (prefers-reduced-motion) {
.blocklyBlockCanvas.blocklyCanvasTransitioning,
.blocklyBubbleCanvas.blocklyCanvasTransitioning {
transition: none;
}
}

.blocklyEmboss {
Expand Down
74 changes: 14 additions & 60 deletions packages/blockly/core/flyout_base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import * as dom from './utils/dom.js';
import * as idGenerator from './utils/idgenerator.js';
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';

/**
Expand Down Expand Up @@ -813,44 +812,23 @@ export abstract class Flyout
* @internal
*/
createBlock(originalBlock: BlockSvg): BlockSvg {
let newBlock = null;
eventUtils.disable();
const variablesBeforeCreation = this.targetWorkspace
.getVariableMap()
.getAllVariables();
this.targetWorkspace.setResizesEnabled(false);
try {
newBlock = this.placeNewBlock(originalBlock);
} finally {
eventUtils.enable();
const targetWorkspace = this.targetWorkspace;
const svgRootOld = originalBlock.getSvgRoot();
if (!svgRootOld) {
throw Error('oldBlock is not rendered');
}

// Close the flyout.
this.targetWorkspace.hideChaff();

const newVariables = Variables.getAddedVariables(
this.targetWorkspace,
variablesBeforeCreation,
);

if (eventUtils.isEnabled()) {
eventUtils.setGroup(true);
// Fire a VarCreate event for each (if any) new variable created.
for (let i = 0; i < newVariables.length; i++) {
const thisVariable = newVariables[i];
eventUtils.fire(
new (eventUtils.get(EventType.VAR_CREATE))(thisVariable),
);
}
// Clone the block.
const json = this.serializeBlock(originalBlock);
// Normally this resizes leading to weird jumps. Save it for terminateDrag.
targetWorkspace.setResizesEnabled(false);
const block = blocks.appendInternal(json, targetWorkspace, {
recordUndo: true,
}) as BlockSvg;

// Block events come after var events, in case they refer to newly created
// variables.
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_CREATE))(newBlock));
}
if (this.autoClose) {
this.hide();
}
return newBlock;
this.positionNewBlock(originalBlock, block);
targetWorkspace.hideChaff();
return block;
}

/**
Expand All @@ -873,30 +851,6 @@ export abstract class Flyout
: false;
}

/**
* Copy a block from the flyout to the workspace and position it correctly.
*
* @param oldBlock The flyout block to copy.
* @returns The new block in the main workspace.
*/
private placeNewBlock(oldBlock: BlockSvg): BlockSvg {
const targetWorkspace = this.targetWorkspace;
const svgRootOld = oldBlock.getSvgRoot();
if (!svgRootOld) {
throw Error('oldBlock is not rendered');
}

// Clone the block.
const json = this.serializeBlock(oldBlock);
// Normally this resizes leading to weird jumps. Save it for terminateDrag.
targetWorkspace.setResizesEnabled(false);
const block = blocks.append(json, targetWorkspace) as BlockSvg;

this.positionNewBlock(oldBlock, block);

return block;
}

/**
* Serialize a block to JSON.
*
Expand Down
8 changes: 7 additions & 1 deletion packages/blockly/core/flyout_button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,13 @@ Css.register(`
fill: #666;
}

.blocklyFlyoutButton:hover {
@media (hover: hover) {
.blocklyFlyoutButton:hover {
fill: #aaa;
}
}

.blocklyFlyoutButton:active {
fill: #aaa;
}

Expand Down
84 changes: 28 additions & 56 deletions packages/blockly/core/gesture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as eventUtils from './events/utils.js';
import type {Field} from './field.js';
import {getFocusManager} from './focus_manager.js';
import type {IBubble} from './interfaces/i_bubble.js';
import {hasContextMenu} from './interfaces/i_contextmenu.js';
import {IDraggable, isDraggable} from './interfaces/i_draggable.js';
import {IDragger} from './interfaces/i_dragger.js';
import type {IFlyout} from './interfaces/i_flyout.js';
Expand Down Expand Up @@ -273,24 +274,19 @@ export class Gesture {
throw new Error(`Cannot update dragging from the flyout because the ' +
'flyout's target workspace is undefined`);
}
if (
!this.flyout.isScrollable() ||
this.flyout.isDragTowardWorkspace(this.currentDragDeltaXY)
) {
this.startWorkspace_ = this.flyout.targetWorkspace;
this.startWorkspace_.updateScreenCalculationsIfScrolled();
// Start the event group now, so that the same event group is used for
// block creation and block dragging.
if (!eventUtils.getGroup()) {
eventUtils.setGroup(true);
}
// The start block is no longer relevant, because this is a drag.
this.startBlock = null;
this.targetBlock = this.flyout.createBlock(this.targetBlock);
getFocusManager().focusNode(this.targetBlock);
return true;

this.startWorkspace_ = this.flyout.targetWorkspace;
this.startWorkspace_.updateScreenCalculationsIfScrolled();
// Start the event group now, so that the same event group is used for
// block creation and block dragging.
if (!eventUtils.getGroup()) {
eventUtils.setGroup(true);
}
return false;
// The start block is no longer relevant, because this is a drag.
this.startBlock = null;
this.targetBlock = this.flyout.createBlock(this.targetBlock);
getFocusManager().focusNode(this.targetBlock);
return true;
}

/**
Expand Down Expand Up @@ -732,22 +728,12 @@ export class Gesture {
* @internal
*/
handleRightClick(e: PointerEvent) {
if (this.targetBlock) {
this.bringBlockToFront();
this.targetBlock.workspace.hideChaff(!!this.flyout);
this.targetBlock.showContextMenu(e);
} else if (this.startBubble) {
this.startBubble.showContextMenu(e);
} else if (this.startComment) {
this.startComment.workspace.hideChaff();
this.startComment.showContextMenu(e);
} else if (this.startWorkspace_ && !this.flyout) {
this.startWorkspace_.hideChaff();
getFocusManager().focusNode(this.startWorkspace_);
this.startWorkspace_.showContextMenu(e);
const selection = getFocusManager().getFocusedNode();
if (hasContextMenu(selection)) {
this.startWorkspace_?.hideChaff(!!this.flyout);
selection.showContextMenu(e);
}

// TODO: Handle right-click on a bubble.
e.preventDefault();
e.stopPropagation();

Expand All @@ -773,7 +759,12 @@ export class Gesture {
this.setStartWorkspace(ws);
this.mostRecentEvent = e;

if (!this.targetBlock && !this.startBubble && !this.startComment) {
if (
!this.targetBlock &&
!this.startBubble &&
!this.startComment &&
!this.startIcon
) {
// Ensure the workspace is selected if nothing else should be. Note that
// this is focusNode() instead of focusTree() because if any active node
// is focused in the workspace it should be defocused.
Expand Down Expand Up @@ -878,12 +869,6 @@ export class Gesture {
);
}

// Note that the order is important here: bringing a block to the front will
// cause it to become focused and showing the field editor will capture
// focus ephemerally. It's important to ensure that focus is properly
// restored back to the block after field editing has completed.
this.bringBlockToFront();

// Only show the editor if the field's editor wasn't already open
// right before this gesture started.
const dropdownAlreadyOpen = this.currentDropdownOwner === this.startField;
Expand All @@ -899,7 +884,6 @@ export class Gesture {
'Cannot do an icon click because the start icon is undefined',
);
}
this.bringBlockToFront();
this.startIcon.onClick();
}

Expand Down Expand Up @@ -938,7 +922,6 @@ export class Gesture {
);
eventUtils.fire(event);
}
this.bringBlockToFront();
eventUtils.setGroup(false);
}

Expand All @@ -957,19 +940,6 @@ export class Gesture {

// TODO (fenichel): Move bubbles to the front.

/**
* Move the dragged/clicked block to the front of the workspace so that it is
* not occluded by other blocks.
*/
private bringBlockToFront() {
// Blocks in the flyout don't overlap, so skip the work.
if (this.targetBlock && !this.flyout) {
// Always ensure the block being dragged/clicked has focus.
getFocusManager().focusNode(this.targetBlock);
this.targetBlock.bringToFront();
}
}

/* Begin functions for populating a gesture at pointerdown. */

/**
Expand Down Expand Up @@ -1039,8 +1009,9 @@ export class Gesture {
* @internal
*/
setStartBlock(block: BlockSvg) {
// If the gesture already went through a bubble, don't set the start block.
if (!this.startBlock && !this.startBubble) {
// If the gesture already went through a block child, don't set the start
// block.
if (!this.startBlock && !this.startBubble && !this.startIcon) {
this.startBlock = block;
if (block.isInFlyout && block !== block.getRootBlock()) {
this.setTargetBlock(block.getRootBlock());
Expand All @@ -1064,7 +1035,8 @@ export class Gesture {
this.setTargetBlock(block.getParent()!);
} else {
this.targetBlock = block;
getFocusManager().focusNode(block);
getFocusManager().focusNode(this.targetBlock);
this.targetBlock.bringToFront();
}
}

Expand Down
7 changes: 6 additions & 1 deletion packages/blockly/core/icons/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import type {Block} from '../block.js';
import type {BlockSvg} from '../block_svg.js';
import * as browserEvents from '../browser_events.js';
import type {IContextMenu} from '../interfaces/i_contextmenu.js';
import type {IFocusableTree} from '../interfaces/i_focusable_tree.js';
import {hasBubble} from '../interfaces/i_has_bubble.js';
import type {IIcon} from '../interfaces/i_icon.js';
Expand All @@ -26,7 +27,7 @@ import type {IconType} from './icon_types.js';
* block (such as warnings or comments) as opposed to fields, which provide
* "actual" information, related to how a block functions.
*/
export abstract class Icon implements IIcon {
export abstract class Icon implements IIcon, IContextMenu {
/**
* The position of this icon relative to its blocks top-start,
* in workspace units.
Expand Down Expand Up @@ -196,4 +197,8 @@ export abstract class Icon implements IIcon {
getSourceBlock(): Block {
return this.sourceBlock;
}

showContextMenu(e: PointerEvent) {
(this.getSourceBlock() as BlockSvg).showContextMenu(e);
}
}
5 changes: 5 additions & 0 deletions packages/blockly/core/interfaces/i_contextmenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ export interface IContextMenu {
*/
showContextMenu(e: Event): void;
}

/** @returns true if the given object implements IContextMenu. */
export function hasContextMenu(obj: any): obj is IContextMenu {
return obj && typeof obj.showContextMenu === 'function';
}
14 changes: 8 additions & 6 deletions packages/blockly/core/layer_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ export class LayerManager {
* @internal
*/
appendToAnimationLayer(elem: IRenderedElement) {
const currentTransform = this.dragLayer?.getAttribute('transform');
const currentTransform = this.dragLayer?.style.transform;
// Only update the current transform when appending, so animations don't
// move if the workspace moves.
if (currentTransform) {
this.animationLayer?.setAttribute('transform', currentTransform);
if (currentTransform && this.animationLayer) {
this.animationLayer.style.transform = currentTransform;
}
this.animationLayer?.appendChild(elem.getSvgRoot());
}
Expand All @@ -88,10 +88,12 @@ export class LayerManager {
* @internal
*/
translateLayers(newCoord: Coordinate, newScale: number) {
const translation = `translate(${newCoord.x}, ${newCoord.y}) scale(${newScale})`;
this.dragLayer?.setAttribute('transform', translation);
const translation = `translate(${newCoord.x}px, ${newCoord.y}px) scale(${newScale})`;
if (this.dragLayer) {
this.dragLayer.style.transform = translation;
}
for (const [_, layer] of this.layers) {
layer.setAttribute('transform', translation);
layer.style.transform = translation;
}
}

Expand Down
Loading
Loading