scratch-blocks 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scratch-blocks",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "Scratch Blocks is a library for building creative computing interfaces.",
5
5
  "author": "Massachusetts Institute of Technology",
6
6
  "license": "Apache-2.0",
@@ -28,6 +28,10 @@ import * as Constants from '../constants'
28
28
  import { FlyoutCheckboxIcon } from '../flyout_checkbox_icon'
29
29
  import { ScratchProcedures } from '../procedures'
30
30
 
31
+ interface ScratchBlockSvg extends Blockly.BlockSvg {
32
+ hatStylePersisted?: boolean
33
+ }
34
+
31
35
  /**
32
36
  * Helper function that generates an extension based on a category name.
33
37
  * The generated function will set the block's style based on the category name.
@@ -61,26 +65,46 @@ const SHAPE_STATEMENT = function (this: Blockly.Block) {
61
65
  this.setNextStatement(true, null)
62
66
  }
63
67
 
68
+ /**
69
+ * Build an extension to make a block be shaped as a hat block, based on the given hat type.
70
+ * Replaces the block's setStyle function to ensure that the hat shape is preserved
71
+ * even if the style is changed or rebuilt.
72
+ * @param hatType the type of hat: 'cap' for regular Scratch hat blocks, or 'bowler' for procedure definitions.
73
+ * @returns A Blockly extension implementing the requested hat type.
74
+ */
75
+ const makeHatExtension = function (hatType: string) {
76
+ return function (this: Blockly.Block) {
77
+ this.setInputsInline(true)
78
+ this.setNextStatement(true, null)
79
+ this.hat = hatType
80
+ // When the workspace theme is refreshed (e.g. when an extension is loaded),
81
+ // Blockly calls setStyle() on all workspace blocks. This resets block.hat to
82
+ // the style's hat value, which is '' for most styles, erasing the hat set
83
+ // above. Override setStyle on this instance to re-apply the hat afterward.
84
+ const blockSvg = this as ScratchBlockSvg
85
+ if (!blockSvg.hatStylePersisted) {
86
+ const origSetStyle = blockSvg.setStyle.bind(blockSvg)
87
+ blockSvg.setStyle = function (blockStyleName: string) {
88
+ origSetStyle(blockStyleName)
89
+ blockSvg.hat = hatType
90
+ }
91
+ blockSvg.hatStylePersisted = true
92
+ }
93
+ }
94
+ }
95
+
64
96
  /**
65
97
  * Extension to make a block be shaped as a hat block, regardless of its
66
98
  * inputs. That means the block should have a next connection and have inline
67
99
  * inputs, but have no previous connection.
68
100
  */
69
- const SHAPE_HAT = function (this: Blockly.Block) {
70
- this.setInputsInline(true)
71
- this.setNextStatement(true, null)
72
- this.hat = 'cap'
73
- }
101
+ const SHAPE_HAT = makeHatExtension('cap')
74
102
 
75
103
  /**
76
104
  * Extension to make a block be shaped as a bowler hat block, with rounded
77
105
  * corners on both sides and no indentation for statement blocks.
78
106
  */
79
- const SHAPE_BOWLER_HAT = function (this: Blockly.Block) {
80
- this.setInputsInline(true)
81
- this.setNextStatement(true, null)
82
- this.hat = 'bowler'
83
- }
107
+ const SHAPE_BOWLER_HAT = makeHatExtension('bowler')
84
108
 
85
109
  /**
86
110
  * Extension to make a block be shaped as an end block, regardless of its
package/src/index.ts CHANGED
@@ -157,3 +157,18 @@ contextMenuItems.registerDuplicateBlock()
157
157
  Blockly.ContextMenuRegistry.registry.unregister('workspaceDelete')
158
158
  contextMenuItems.registerDeleteAll()
159
159
  Blockly.comments.CommentView.defaultCommentSize = new Blockly.utils.Size(200, 200)
160
+
161
+ // When the focused block is deleted and has no parent or nearby neighbor,
162
+ // Blockly falls back to focusing the first/topmost block in the workspace,
163
+ // which triggers a scroll to that block. In Scratch, focus should fall back
164
+ // to the workspace itself (whose onNodeFocus is a no-op) rather than to a
165
+ // specific block, so deleting a block doesn't reset the scroll position.
166
+ // We may need to re-evaluate this when we explicitly work on keyboard navigation.
167
+ const originalGetRestoredFocusableNode =
168
+ Blockly.WorkspaceSvg.prototype.getRestoredFocusableNode
169
+ Blockly.WorkspaceSvg.prototype.getRestoredFocusableNode = function (
170
+ previousNode,
171
+ ) {
172
+ if (!previousNode && !this.isFlyout) return null
173
+ return originalGetRestoredFocusableNode.call(this, previousNode)
174
+ }
@@ -14,10 +14,11 @@ class ScratchConnectionChecker extends Blockly.ConnectionChecker {
14
14
  isDragging: boolean,
15
15
  opt_distance?: number,
16
16
  ): number {
17
- // The prototype block is visual-only and should not accept any connections.
18
- const isPrototypeConnection = (c: Blockly.Connection | null) =>
19
- c?.getSourceBlock().type === 'procedures_prototype'
20
- if (isPrototypeConnection(a) || isPrototypeConnection(b)) {
17
+ // The prototype's next connection is visual-only and should not accept any connections.
18
+ const isPrototypeNextConn = (c: Blockly.Connection | null) =>
19
+ c?.type === Blockly.ConnectionType.NEXT_STATEMENT &&
20
+ c.getSourceBlock().type === 'procedures_prototype'
21
+ if (isPrototypeNextConn(a) || isPrototypeNextConn(b)) {
21
22
  return Blockly.Connection.REASON_CHECKS_FAILED
22
23
  }
23
24
  return super.canConnectWithReason(a, b, isDragging, opt_distance)