66
77import {
88 ASTNode ,
9+ Events ,
910 ShortcutRegistry ,
1011 utils as BlocklyUtils ,
1112 dialog ,
@@ -126,6 +127,9 @@ export class EnterAction {
126127 * the block will be placed on.
127128 */
128129 private insertFromFlyout ( workspace : WorkspaceSvg ) {
130+ workspace . setResizesEnabled ( false ) ;
131+ Events . setGroup ( true ) ;
132+
129133 const stationaryNode = this . navigation . getStationaryNode ( workspace ) ;
130134 const newBlock = this . createNewBlock ( workspace ) ;
131135 if ( ! newBlock ) return ;
@@ -137,8 +141,99 @@ export class EnterAction {
137141 }
138142 }
139143
144+ if ( workspace . getTopBlocks ( ) . includes ( newBlock ) ) {
145+ this . positionNewTopLevelBlock ( workspace , newBlock ) ;
146+ }
147+
148+ Events . setGroup ( false ) ;
149+ workspace . setResizesEnabled ( true ) ;
150+
140151 this . navigation . focusWorkspace ( workspace ) ;
141- workspace . getCursor ( ) ?. setCurNode ( ASTNode . createBlockNode ( newBlock ) ) ;
152+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
153+ workspace . getCursor ( ) ?. setCurNode ( ASTNode . createBlockNode ( newBlock ) ! ) ;
154+ }
155+
156+ /**
157+ * Position a new top-level block to avoid overlap at the top left.
158+ *
159+ * Similar to `WorkspaceSvg.cleanUp()` but does not constrain itself to not
160+ * affecting code ordering in order to use horizontal space.
161+ *
162+ * @param workspace The workspace.
163+ * @param newBlock The top-level block to move to free space.
164+ */
165+ private positionNewTopLevelBlock (
166+ workspace : WorkspaceSvg ,
167+ newBlock : BlockSvg ,
168+ ) {
169+ const initialY = 10 ;
170+ const initialX = 10 ;
171+ const xSpacing = 80 ;
172+
173+ const filteredTopBlocks = workspace
174+ . getTopBlocks ( true )
175+ . filter ( ( block ) => block . id !== newBlock . id ) ;
176+ const allBlockBounds = filteredTopBlocks . map ( ( block ) =>
177+ block . getBoundingRectangle ( ) ,
178+ ) ;
179+
180+ const toolboxWidth = workspace . getToolbox ( ) ?. getWidth ( ) ;
181+ const workspaceWidth =
182+ workspace . getParentSvg ( ) . clientWidth - ( toolboxWidth ?? 0 ) ;
183+ const workspaceHeight = workspace . getParentSvg ( ) . clientHeight ;
184+ const { height : newBlockHeight , width : newBlockWidth } =
185+ newBlock . getHeightWidth ( ) ;
186+
187+ const getNextIntersectingBlock = function (
188+ newBlockRect : BlocklyUtils . Rect ,
189+ ) : BlocklyUtils . Rect | null {
190+ for ( const rect of allBlockBounds ) {
191+ if ( newBlockRect . intersects ( rect ) ) {
192+ return rect ;
193+ }
194+ }
195+ return null ;
196+ } ;
197+
198+ let cursorY = initialY ;
199+ let cursorX = initialX ;
200+ const minBlockHeight = workspace
201+ . getRenderer ( )
202+ . getConstants ( ) . MIN_BLOCK_HEIGHT ;
203+ // Make the initial movement of shifting the block to its best possible position.
204+ let boundingRect = newBlock . getBoundingRectangle ( ) ;
205+ newBlock . moveBy ( cursorX - boundingRect . left , cursorY - boundingRect . top , [
206+ 'cleanup' ,
207+ ] ) ;
208+ newBlock . snapToGrid ( ) ;
209+
210+ boundingRect = newBlock . getBoundingRectangle ( ) ;
211+ let conflictingRect = getNextIntersectingBlock ( boundingRect ) ;
212+ while ( conflictingRect != null ) {
213+ const newCursorX =
214+ conflictingRect . left + conflictingRect . getWidth ( ) + xSpacing ;
215+ const newCursorY =
216+ conflictingRect . top + conflictingRect . getHeight ( ) + minBlockHeight ;
217+ if ( newCursorX + newBlockWidth <= workspaceWidth ) {
218+ cursorX = newCursorX ;
219+ } else if ( newCursorY + newBlockHeight <= workspaceHeight ) {
220+ cursorY = newCursorY ;
221+ cursorX = initialX ;
222+ } else {
223+ // Off screen, but new blocks will be selected which will scroll them
224+ // into view.
225+ cursorY = newCursorY ;
226+ cursorX = initialX ;
227+ }
228+ newBlock . moveBy ( cursorX - boundingRect . left , cursorY - boundingRect . top , [
229+ 'cleanup' ,
230+ ] ) ;
231+ newBlock . snapToGrid ( ) ;
232+ boundingRect = newBlock . getBoundingRectangle ( ) ;
233+ conflictingRect = getNextIntersectingBlock ( boundingRect ) ;
234+ }
235+
236+ newBlock . bringToFront ( ) ;
142237 }
143238
144239 /**
0 commit comments