44 * SPDX-License-Identifier: Apache-2.0
55 */
66
7- import {
8- ASTNode ,
9- ContextMenu ,
10- ContextMenuRegistry ,
11- ShortcutRegistry ,
12- utils as BlocklyUtils ,
13- WidgetDiv ,
14- } from 'blockly' ;
7+ import { ShortcutRegistry , utils as BlocklyUtils , WidgetDiv } from 'blockly' ;
158import * as Constants from '../constants' ;
16- import type { BlockSvg , RenderedConnection , WorkspaceSvg } from 'blockly' ;
9+ import type { WorkspaceSvg } from 'blockly' ;
1710import { Navigation } from '../navigation' ;
1811
1912const KeyCodes = BlocklyUtils . KeyCodes ;
@@ -85,50 +78,20 @@ export class ActionMenu {
8578 * @param workspace The workspace.
8679 */
8780 private openActionMenu ( workspace : WorkspaceSvg ) : boolean {
88- let rtl : boolean ;
89-
9081 // TODO(#362): Pass this through the precondition and callback instead of making it up.
9182 const menuOpenEvent = new KeyboardEvent ( 'keydown' ) ;
9283
9384 const cursor = workspace . getCursor ( ) ;
9485 if ( ! cursor ) throw new Error ( 'workspace has no cursor' ) ;
9586 const node = cursor . getCurNode ( ) ;
9687 if ( ! node ) return false ;
97- const nodeType = node . getType ( ) ;
98- switch ( nodeType ) {
99- case ASTNode . types . BLOCK : {
100- const block = node . getLocation ( ) as BlockSvg ;
101- block . showContextMenu ( menuOpenEvent ) ;
102- break ;
103- }
104-
105- // case Blockly.ASTNode.types.INPUT:
106- case ASTNode . types . NEXT :
107- case ASTNode . types . PREVIOUS :
108- case ASTNode . types . INPUT : {
109- const connection = node . getLocation ( ) as RenderedConnection ;
110- rtl = connection . getSourceBlock ( ) . RTL ;
111-
112- const menuOptions = ContextMenuRegistry . registry . getContextMenuOptions (
113- { focusedNode : connection } ,
114- menuOpenEvent ,
115- ) ;
116- // If no valid options, don't show a menu
117- if ( ! menuOptions ?. length ) return true ;
118- const location = this . calculateLocationForConnectionMenu ( connection ) ;
119- ContextMenu . show ( menuOpenEvent , menuOptions , rtl , workspace , location ) ;
120- break ;
121- }
122-
123- case ASTNode . types . WORKSPACE : {
124- const workspace = node . getLocation ( ) as WorkspaceSvg ;
125- workspace . showContextMenu ( menuOpenEvent ) ;
126- break ;
127- }
128-
129- default :
130- console . info ( `No action menu for ASTNode of type ${ nodeType } ` ) ;
131- return false ;
88+ // TODO(google/blockly#8847): Add typeguard for IContextMenu in core when this moves over
89+ const location = node . getLocation ( ) as any ;
90+ if ( location . showContextMenu ) {
91+ location . showContextMenu ( menuOpenEvent ) ;
92+ } else {
93+ console . info ( `No action menu for ASTNode of type ${ node . getType ( ) } ` ) ;
94+ return false ;
13295 }
13396
13497 setTimeout ( ( ) => {
@@ -147,85 +110,4 @@ export class ActionMenu {
147110 } , 10 ) ;
148111 return true ;
149112 }
150-
151- /**
152- * Find a context menu action, throwing an `Error` if it is not present or
153- * not an action. This usefully narrows the type to `ActionRegistryItem`
154- * which is not exported from Blockly.
155- *
156- * @param id The id of the action.
157- * @returns the action.
158- */
159- private getContextMenuAction ( id : string ) {
160- const item = ContextMenuRegistry . registry . getItem ( id ) ;
161- if ( ! item ) {
162- throw new Error ( `can't find context menu item ${ id } ` ) ;
163- }
164- if ( ! item ?. callback ) {
165- throw new Error ( `context menu item unexpectedly not action ${ id } ` ) ;
166- }
167- return item ;
168- }
169-
170- /**
171- * Create a fake PointerEvent for opening the action menu on the specified
172- * block.
173- *
174- * @param block The block to open the action menu for.
175- * @returns screen coordinates of where to show a menu for a block
176- */
177- private calculateLocationOfBlock ( block : BlockSvg ) : BlocklyUtils . Coordinate {
178- // Get the location of the top-left corner of the block in
179- // screen coordinates.
180- const blockCoords = BlocklyUtils . svgMath . wsToScreenCoordinates (
181- block . workspace ,
182- block . getRelativeToSurfaceXY ( ) ,
183- ) ;
184-
185- // Prefer a y position below the first field in the block.
186- const fieldBoundingClientRect = block . inputList
187- . filter ( ( input ) => input . isVisible ( ) )
188- . flatMap ( ( input ) => input . fieldRow )
189- . filter ( ( f ) => f . isVisible ( ) ) [ 0 ]
190- ?. getSvgRoot ( )
191- ?. getBoundingClientRect ( ) ;
192-
193- const y =
194- fieldBoundingClientRect && fieldBoundingClientRect . height
195- ? fieldBoundingClientRect . y + fieldBoundingClientRect . height
196- : blockCoords . y + block . height ;
197-
198- return new BlocklyUtils . Coordinate ( blockCoords . x + 5 , y + 5 ) ;
199- }
200-
201- /**
202- * Create a fake PointerEvent for opening the action menu for the
203- * given connection.
204- *
205- * For now this just puts the action menu in the same place as the
206- * context menu for the source block.
207- *
208- * @param connection The node to open the action menu for.
209- * @returns Screen coordinates of where to show menu for a connection node.
210- */
211- private calculateLocationForConnectionMenu (
212- connection : RenderedConnection ,
213- ) : BlocklyUtils . Coordinate {
214- const block = connection . getSourceBlock ( ) as BlockSvg ;
215- const workspace = block . workspace as WorkspaceSvg ;
216-
217- if ( typeof connection . x !== 'number' ) {
218- // No coordinates for connection? Fall back to the parent block.
219- return this . calculateLocationOfBlock ( block ) ;
220- }
221- const connectionWSCoords = new BlocklyUtils . Coordinate (
222- connection . x ,
223- connection . y ,
224- ) ;
225- const connectionScreenCoords = BlocklyUtils . svgMath . wsToScreenCoordinates (
226- workspace ,
227- connectionWSCoords ,
228- ) ;
229- return connectionScreenCoords . translate ( 5 , 5 ) ;
230- }
231113}
0 commit comments