Skip to content

Commit 6916afc

Browse files
chore: create fake events for blocks and connections instead of nodes (#321)
* chore: create fake event for block instead of node * chore: create fake event for connection instead of node
1 parent 439e89a commit 6916afc

File tree

1 file changed

+61
-55
lines changed

1 file changed

+61
-55
lines changed

src/actions/action_menu.ts

Lines changed: 61 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
WidgetDiv,
1616
} from 'blockly';
1717
import * as Constants from '../constants';
18-
import type {BlockSvg, WorkspaceSvg} from 'blockly';
18+
import type {BlockSvg, RenderedConnection, WorkspaceSvg} from 'blockly';
1919
import {Navigation} from '../navigation';
2020

2121
const KeyCodes = BlocklyUtils.KeyCodes;
@@ -140,34 +140,7 @@ export class ActionMenu {
140140
// Slightly hacky: get insert action from registry. Hacky
141141
// because registry typings don't include {connection: ...} as
142142
// a possible kind of scope.
143-
const insertAction = ContextMenuRegistry.registry.getItem('insert');
144-
if (!insertAction) throw new Error("can't find insert action");
145-
146-
const pasteAction = ContextMenuRegistry.registry.getItem(
147-
'blockPasteFromContextMenu',
148-
);
149-
if (!pasteAction) throw new Error("can't find paste action");
150-
const possibleOptions = [insertAction, pasteAction /* etc.*/];
151-
152-
// Check preconditions and get menu texts.
153-
const scope = {
154-
connection,
155-
} as unknown as ContextMenuRegistry.Scope;
156-
for (const option of possibleOptions) {
157-
const precondition = option.preconditionFn(scope);
158-
if (precondition === 'hidden') continue;
159-
const displayText =
160-
typeof option.displayText === 'function'
161-
? option.displayText(scope)
162-
: option.displayText;
163-
menuOptions.push({
164-
text: displayText,
165-
enabled: precondition === 'enabled',
166-
callback: option.callback,
167-
scope,
168-
weight: option.weight,
169-
});
170-
}
143+
this.addConnectionItems(connection, menuOptions);
171144
break;
172145

173146
default:
@@ -195,6 +168,50 @@ export class ActionMenu {
195168
return true;
196169
}
197170

171+
/**
172+
* Add menu items for a context menu on a connection scope.
173+
*
174+
* @param connection The connection on which the menu is shown.
175+
* @param menuOptions The list of options, which may be modified by this method.
176+
*/
177+
private addConnectionItems(
178+
connection: Connection,
179+
menuOptions: (
180+
| ContextMenuRegistry.ContextMenuOption
181+
| ContextMenuRegistry.LegacyContextMenuOption
182+
)[],
183+
) {
184+
const insertAction = ContextMenuRegistry.registry.getItem('insert');
185+
if (!insertAction) throw new Error("can't find insert action");
186+
187+
const pasteAction = ContextMenuRegistry.registry.getItem(
188+
'blockPasteFromContextMenu',
189+
);
190+
if (!pasteAction) throw new Error("can't find paste action");
191+
const possibleOptions = [insertAction, pasteAction /* etc.*/];
192+
193+
// Check preconditions and get menu texts.
194+
const scope = {
195+
connection,
196+
} as unknown as ContextMenuRegistry.Scope;
197+
for (const option of possibleOptions) {
198+
const precondition = option.preconditionFn(scope);
199+
if (precondition === 'hidden') continue;
200+
const displayText =
201+
typeof option.displayText === 'function'
202+
? option.displayText(scope)
203+
: option.displayText;
204+
menuOptions.push({
205+
text: displayText,
206+
enabled: precondition === 'enabled',
207+
callback: option.callback,
208+
scope,
209+
weight: option.weight,
210+
});
211+
}
212+
return menuOptions;
213+
}
214+
198215
/**
199216
* Create a fake PointerEvent for opening the action menu for the
200217
* given ASTNode.
@@ -205,31 +222,28 @@ export class ActionMenu {
205222
private fakeEventForNode(node: ASTNode): PointerEvent {
206223
switch (node.getType()) {
207224
case ASTNode.types.BLOCK:
208-
return this.fakeEventForBlockNode(node);
225+
return this.fakeEventForBlock(node.getLocation() as BlockSvg);
209226
case ASTNode.types.NEXT:
210227
case ASTNode.types.PREVIOUS:
211228
case ASTNode.types.INPUT:
212-
return this.fakeEventForConnectionNode(node);
229+
return this.fakeEventForConnectionNode(
230+
node.getLocation() as RenderedConnection,
231+
);
213232
default:
214233
throw new TypeError('unhandled node type');
215234
}
216235
}
217236

218237
/**
219-
* Create a fake PointerEvent for opening the action menu for the
220-
* given ASTNode of type BLOCK.
238+
* Create a fake PointerEvent for opening the action menu on the specified
239+
* block.
221240
*
222-
* @param node The node to open the action menu for.
241+
* @param block The block to open the action menu for.
223242
* @returns A synthetic pointerdown PointerEvent.
224243
*/
225-
private fakeEventForBlockNode(node: ASTNode): PointerEvent {
226-
if (node.getType() !== ASTNode.types.BLOCK) {
227-
throw new TypeError('can only create PointerEvents for BLOCK nodes');
228-
}
229-
244+
private fakeEventForBlock(block: BlockSvg) {
230245
// Get the location of the top-left corner of the block in
231246
// screen coordinates.
232-
const block = node.getLocation() as BlockSvg;
233247
const blockCoords = BlocklyUtils.svgMath.wsToScreenCoordinates(
234248
block.workspace,
235249
block.getRelativeToSurfaceXY(),
@@ -257,31 +271,23 @@ export class ActionMenu {
257271

258272
/**
259273
* Create a fake PointerEvent for opening the action menu for the
260-
* given ASTNode of type NEXT, PREVIOUS or INPUT.
274+
* given connection.
261275
*
262276
* For now this just puts the action menu in the same place as the
263277
* context menu for the source block.
264278
*
265-
* @param node The node to open the action menu for.
279+
* @param connection The node to open the action menu for.
266280
* @returns A synthetic pointerdown PointerEvent.
267281
*/
268-
private fakeEventForConnectionNode(node: ASTNode): PointerEvent {
269-
if (
270-
node.getType() !== ASTNode.types.NEXT &&
271-
node.getType() !== ASTNode.types.PREVIOUS &&
272-
node.getType() !== ASTNode.types.INPUT
273-
) {
274-
throw new TypeError('can only create PointerEvents for connection nodes');
275-
}
276-
277-
const connection = node.getLocation() as Connection;
278-
const block = connection.getSourceBlock();
282+
private fakeEventForConnectionNode(
283+
connection: RenderedConnection,
284+
): PointerEvent {
285+
const block = connection.getSourceBlock() as BlockSvg;
279286
const workspace = block.workspace as WorkspaceSvg;
280287

281288
if (typeof connection.x !== 'number') {
282289
// No coordinates for connection? Fall back to the parent block.
283-
const blockNode = new ASTNode(ASTNode.types.BLOCK, block);
284-
return this.fakeEventForBlockNode(blockNode);
290+
return this.fakeEventForBlock(block);
285291
}
286292
const connectionWSCoords = new BlocklyUtils.Coordinate(
287293
connection.x,

0 commit comments

Comments
 (0)