Skip to content

Commit e03d526

Browse files
authored
release: v11.1.0
Merge pull request #8185 from google/rc/v11.1.0
2 parents 9519333 + 4f596aa commit e03d526

22 files changed

Lines changed: 247 additions & 95 deletions

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto

core/block.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -675,17 +675,6 @@ export class Block implements IASTNodeLocation {
675675
return block;
676676
}
677677

678-
/**
679-
* Returns this block if it is a shadow block, or the first non-shadow parent.
680-
*
681-
* @internal
682-
*/
683-
getFirstNonShadowBlock(): this {
684-
if (!this.isShadow()) return this;
685-
// We can assert the parent is non-null because shadows must have parents.
686-
return this.getParent()!.getFirstNonShadowBlock();
687-
}
688-
689678
/**
690679
* Find all the blocks that are directly nested inside this one.
691680
* Includes value and statement inputs, as well as any following statement.

core/block_svg.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ export class BlockSvg
599599
const menuOptions = this.generateContextMenu();
600600

601601
if (menuOptions && menuOptions.length) {
602-
ContextMenu.show(e, menuOptions, this.RTL);
602+
ContextMenu.show(e, menuOptions, this.RTL, this.workspace);
603603
ContextMenu.setCurrentBlock(this);
604604
}
605605
}

core/comments/rendered_workspace_comment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export class RenderedWorkspaceComment
274274
ContextMenuRegistry.ScopeType.COMMENT,
275275
{comment: this},
276276
);
277-
contextMenu.show(e, menuOptions, this.workspace.RTL);
277+
contextMenu.show(e, menuOptions, this.workspace.RTL, this.workspace);
278278
}
279279

280280
/** Snap this comment to the nearest grid point. */

core/contextmenu.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {Rect} from './utils/rect.js';
2323
import * as serializationBlocks from './serialization/blocks.js';
2424
import * as svgMath from './utils/svg_math.js';
2525
import * as WidgetDiv from './widgetdiv.js';
26+
import type {WorkspaceSvg} from './workspace_svg.js';
2627
import * as Xml from './xml.js';
2728
import * as common from './common.js';
2829

@@ -62,13 +63,15 @@ let menu_: Menu | null = null;
6263
* @param e Mouse event.
6364
* @param options Array of menu options.
6465
* @param rtl True if RTL, false if LTR.
66+
* @param workspace The workspace associated with the context menu, if any.
6567
*/
6668
export function show(
6769
e: PointerEvent,
6870
options: (ContextMenuOption | LegacyContextMenuOption)[],
6971
rtl: boolean,
72+
workspace?: WorkspaceSvg,
7073
) {
71-
WidgetDiv.show(dummyOwner, rtl, dispose);
74+
WidgetDiv.show(dummyOwner, rtl, dispose, workspace);
7275
if (!options.length) {
7376
hide();
7477
return;

core/css.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ let content = `
307307
.blocklyMinimalBody {
308308
margin: 0;
309309
padding: 0;
310+
height: 100%;
310311
}
311312
312313
.blocklyHtmlInput {

core/dragging/block_drag_strategy.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,24 @@ export class BlockDragStrategy implements IDragStrategy {
5555

5656
private dragging = false;
5757

58+
/**
59+
* If this is a shadow block, the offset between this block and the parent
60+
* block, to add to the drag location. In workspace units.
61+
*/
62+
private dragOffset = new Coordinate(0, 0);
63+
5864
constructor(private block: BlockSvg) {
5965
this.workspace = block.workspace;
6066
}
6167

6268
/** Returns true if the block is currently movable. False otherwise. */
6369
isMovable(): boolean {
70+
if (this.block.isShadow()) {
71+
return this.block.getParent()?.isMovable() ?? false;
72+
}
73+
6474
return (
6575
this.block.isOwnMovable() &&
66-
!this.block.isShadow() &&
6776
!this.block.isDeadOrDying() &&
6877
!this.workspace.options.readOnly &&
6978
// We never drag blocks in the flyout, only create new blocks that are
@@ -77,6 +86,11 @@ export class BlockDragStrategy implements IDragStrategy {
7786
* from any parent blocks.
7887
*/
7988
startDrag(e?: PointerEvent): void {
89+
if (this.block.isShadow()) {
90+
this.startDraggingShadow(e);
91+
return;
92+
}
93+
8094
this.dragging = true;
8195
if (!eventUtils.getGroup()) {
8296
eventUtils.setGroup(true);
@@ -106,6 +120,22 @@ export class BlockDragStrategy implements IDragStrategy {
106120
this.workspace.getLayerManager()?.moveToDragLayer(this.block);
107121
}
108122

123+
/** Starts a drag on a shadow, recording the drag offset. */
124+
private startDraggingShadow(e?: PointerEvent) {
125+
const parent = this.block.getParent();
126+
if (!parent) {
127+
throw new Error(
128+
'Tried to drag a shadow block with no parent. ' +
129+
'Shadow blocks should always have parents.',
130+
);
131+
}
132+
this.dragOffset = Coordinate.difference(
133+
parent.getRelativeToSurfaceXY(),
134+
this.block.getRelativeToSurfaceXY(),
135+
);
136+
parent.startDrag(e);
137+
}
138+
109139
/**
110140
* Whether or not we should disconnect the block when a drag is started.
111141
*
@@ -174,6 +204,11 @@ export class BlockDragStrategy implements IDragStrategy {
174204

175205
/** Moves the block and updates any connection previews. */
176206
drag(newLoc: Coordinate): void {
207+
if (this.block.isShadow()) {
208+
this.block.getParent()?.drag(Coordinate.sum(newLoc, this.dragOffset));
209+
return;
210+
}
211+
177212
this.block.moveDuringDrag(newLoc);
178213
this.updateConnectionPreview(
179214
this.block,
@@ -317,7 +352,12 @@ export class BlockDragStrategy implements IDragStrategy {
317352
* Cleans up any state at the end of the drag. Applies any pending
318353
* connections.
319354
*/
320-
endDrag(): void {
355+
endDrag(e?: PointerEvent): void {
356+
if (this.block.isShadow()) {
357+
this.block.getParent()?.endDrag(e);
358+
return;
359+
}
360+
321361
this.fireDragEndEvent();
322362
this.fireMoveEvent();
323363

@@ -373,6 +413,11 @@ export class BlockDragStrategy implements IDragStrategy {
373413
* including reconnecting connections.
374414
*/
375415
revertDrag(): void {
416+
if (this.block.isShadow()) {
417+
this.block.getParent()?.revertDrag();
418+
return;
419+
}
420+
376421
this.startChildConn?.connect(this.block.nextConnection);
377422
if (this.startParentConn) {
378423
switch (this.startParentConn.type) {

core/dragging/dragger.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,25 @@ export class Dragger implements IDragger {
4242
*/
4343
onDrag(e: PointerEvent, totalDelta: Coordinate) {
4444
this.moveDraggable(e, totalDelta);
45+
const root = this.getRoot(this.draggable);
4546

4647
// Must check `wouldDelete` before calling other hooks on drag targets
4748
// since we have documented that we would do so.
48-
if (isDeletable(this.draggable)) {
49-
this.draggable.setDeleteStyle(
50-
this.wouldDeleteDraggable(e, this.draggable),
51-
);
49+
if (isDeletable(root)) {
50+
root.setDeleteStyle(this.wouldDeleteDraggable(e, root));
5251
}
5352
this.updateDragTarget(e);
5453
}
5554

5655
/** Updates the drag target under the pointer (if there is one). */
5756
protected updateDragTarget(e: PointerEvent) {
5857
const newDragTarget = this.workspace.getDragTarget(e);
58+
const root = this.getRoot(this.draggable);
5959
if (this.dragTarget !== newDragTarget) {
60-
this.dragTarget?.onDragExit(this.draggable);
61-
newDragTarget?.onDragEnter(this.draggable);
60+
this.dragTarget?.onDragExit(root);
61+
newDragTarget?.onDragEnter(root);
6262
}
63-
newDragTarget?.onDragOver(this.draggable);
63+
newDragTarget?.onDragOver(root);
6464
this.dragTarget = newDragTarget;
6565
}
6666

@@ -80,7 +80,7 @@ export class Dragger implements IDragger {
8080
*/
8181
protected wouldDeleteDraggable(
8282
e: PointerEvent,
83-
draggable: IDraggable & IDeletable,
83+
rootDraggable: IDraggable & IDeletable,
8484
) {
8585
const dragTarget = this.workspace.getDragTarget(e);
8686
if (!dragTarget) return false;
@@ -92,50 +92,56 @@ export class Dragger implements IDragger {
9292
);
9393
if (!isDeleteArea) return false;
9494

95-
return (dragTarget as IDeleteArea).wouldDelete(draggable);
95+
return (dragTarget as IDeleteArea).wouldDelete(rootDraggable);
9696
}
9797

9898
/** Handles any drag cleanup. */
9999
onDragEnd(e: PointerEvent) {
100100
const origGroup = eventUtils.getGroup();
101101
const dragTarget = this.workspace.getDragTarget(e);
102+
const root = this.getRoot(this.draggable);
103+
102104
if (dragTarget) {
103-
this.dragTarget?.onDrop(this.draggable);
105+
this.dragTarget?.onDrop(root);
104106
}
105107

106-
if (this.shouldReturnToStart(e, this.draggable)) {
108+
if (this.shouldReturnToStart(e, root)) {
107109
this.draggable.revertDrag();
108110
}
109111

110-
const wouldDelete =
111-
isDeletable(this.draggable) &&
112-
this.wouldDeleteDraggable(e, this.draggable);
112+
const wouldDelete = isDeletable(root) && this.wouldDeleteDraggable(e, root);
113113

114114
// TODO(#8148): use a generalized API instead of an instanceof check.
115115
if (wouldDelete && this.draggable instanceof BlockSvg) {
116-
blockAnimations.disposeUiEffect(this.draggable);
116+
blockAnimations.disposeUiEffect(this.draggable.getRootBlock());
117117
}
118118

119119
this.draggable.endDrag(e);
120120

121-
if (wouldDelete && isDeletable(this.draggable)) {
121+
if (wouldDelete && isDeletable(root)) {
122122
// We want to make sure the delete gets grouped with any possible
123123
// move event.
124124
const newGroup = eventUtils.getGroup();
125125
eventUtils.setGroup(origGroup);
126-
this.draggable.dispose();
126+
root.dispose();
127127
eventUtils.setGroup(newGroup);
128128
}
129129
}
130130

131+
// We need to special case blocks for now so that we look at the root block
132+
// instead of the one actually being dragged in most cases.
133+
private getRoot(draggable: IDraggable): IDraggable {
134+
return draggable instanceof BlockSvg ? draggable.getRootBlock() : draggable;
135+
}
136+
131137
/**
132138
* Returns true if we should return the draggable to its original location
133139
* at the end of the drag.
134140
*/
135-
protected shouldReturnToStart(e: PointerEvent, draggable: IDraggable) {
141+
protected shouldReturnToStart(e: PointerEvent, rootDraggable: IDraggable) {
136142
const dragTarget = this.workspace.getDragTarget(e);
137143
if (!dragTarget) return false;
138-
return dragTarget.shouldPreventMove(draggable);
144+
return dragTarget.shouldPreventMove(rootDraggable);
139145
}
140146

141147
protected pixelsToWorkspaceUnits(pixelCoord: Coordinate): Coordinate {

core/field_input.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,12 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
372372
if (!block) {
373373
throw new UnattachedFieldError();
374374
}
375-
WidgetDiv.show(this, block.RTL, this.widgetDispose_.bind(this));
375+
WidgetDiv.show(
376+
this,
377+
block.RTL,
378+
this.widgetDispose_.bind(this),
379+
this.workspace_,
380+
);
376381
this.htmlInput_ = this.widgetCreate_() as HTMLInputElement;
377382
this.isBeingEdited_ = true;
378383
this.valueWhenEditorWasOpened_ = this.value_;
@@ -390,7 +395,7 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
390395
*
391396
* @returns The newly created text input editor.
392397
*/
393-
protected widgetCreate_(): HTMLElement {
398+
protected widgetCreate_(): HTMLInputElement | HTMLTextAreaElement {
394399
const block = this.getSourceBlock();
395400
if (!block) {
396401
throw new UnattachedFieldError();
@@ -546,17 +551,17 @@ export abstract class FieldInput<T extends InputTypes> extends Field<
546551
*/
547552
protected onHtmlInputKeyDown_(e: KeyboardEvent) {
548553
if (e.key === 'Enter') {
549-
WidgetDiv.hide();
554+
WidgetDiv.hideIfOwner(this);
550555
dropDownDiv.hideWithoutAnimation();
551556
} else if (e.key === 'Escape') {
552557
this.setValue(
553558
this.htmlInput_!.getAttribute('data-untyped-default-value'),
554559
false,
555560
);
556-
WidgetDiv.hide();
561+
WidgetDiv.hideIfOwner(this);
557562
dropDownDiv.hideWithoutAnimation();
558563
} else if (e.key === 'Tab') {
559-
WidgetDiv.hide();
564+
WidgetDiv.hideIfOwner(this);
560565
dropDownDiv.hideWithoutAnimation();
561566
(this.sourceBlock_ as BlockSvg).tab(this, !e.shiftKey);
562567
e.preventDefault();

core/flyout_base.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,15 @@ export abstract class Flyout
516516
this.hide();
517517
}
518518

519+
/**
520+
* Get the target workspace inside the flyout.
521+
*
522+
* @returns The target workspace inside the flyout.
523+
*/
524+
getTargetWorkspace(): WorkspaceSvg {
525+
return this.targetWorkspace;
526+
}
527+
519528
/**
520529
* Is the flyout visible?
521530
*

0 commit comments

Comments
 (0)