@@ -15,7 +15,7 @@ import {
1515 WidgetDiv ,
1616} from 'blockly' ;
1717import * as Constants from '../constants' ;
18- import type { BlockSvg , WorkspaceSvg } from 'blockly' ;
18+ import type { BlockSvg , RenderedConnection , WorkspaceSvg } from 'blockly' ;
1919import { Navigation } from '../navigation' ;
2020
2121const 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