blockly 7.20211209.4 → 8.0.0

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.
Files changed (262) hide show
  1. package/blockly.d.ts +18963 -18432
  2. package/blockly.min.js +852 -844
  3. package/blockly_compressed.js +669 -664
  4. package/blockly_compressed.js.map +1 -1
  5. package/blocks/blocks.js +47 -0
  6. package/blocks/colour.js +13 -3
  7. package/blocks/lists.js +22 -13
  8. package/blocks/logic.js +13 -3
  9. package/blocks/loops.js +24 -11
  10. package/blocks/math.js +12 -3
  11. package/blocks/procedures.js +41 -27
  12. package/blocks/text.js +22 -13
  13. package/blocks/variables.js +14 -3
  14. package/blocks/variables_dynamic.js +13 -3
  15. package/blocks_compressed.js +146 -141
  16. package/blocks_compressed.js.map +1 -1
  17. package/core/block.js +1869 -1814
  18. package/core/block_drag_surface.js +201 -200
  19. package/core/block_dragger.js +377 -373
  20. package/core/block_svg.js +1593 -1479
  21. package/core/blockly.js +8 -22
  22. package/core/blocks.js +9 -2
  23. package/core/browser_events.js +22 -5
  24. package/core/bubble.js +841 -797
  25. package/core/bubble_dragger.js +213 -206
  26. package/core/bump_objects.js +2 -2
  27. package/core/clipboard.js +9 -9
  28. package/core/comment.js +353 -332
  29. package/core/common.js +46 -17
  30. package/core/component_manager.js +181 -174
  31. package/core/config.js +87 -0
  32. package/core/connection.js +595 -584
  33. package/core/connection_checker.js +242 -244
  34. package/core/connection_db.js +235 -230
  35. package/core/contextmenu.js +9 -6
  36. package/core/contextmenu_items.js +1 -2
  37. package/core/contextmenu_registry.js +93 -89
  38. package/core/css.js +474 -474
  39. package/core/delete_area.js +45 -42
  40. package/core/drag_target.js +57 -56
  41. package/core/dropdowndiv.js +153 -163
  42. package/core/events/events.js +2 -2
  43. package/core/events/events_abstract.js +89 -77
  44. package/core/events/events_block_base.js +37 -36
  45. package/core/events/events_block_change.js +130 -124
  46. package/core/events/events_block_create.js +73 -71
  47. package/core/events/events_block_delete.js +84 -82
  48. package/core/events/events_block_drag.js +50 -49
  49. package/core/events/events_block_move.js +147 -140
  50. package/core/events/events_bubble_open.js +51 -50
  51. package/core/events/events_click.js +48 -44
  52. package/core/events/events_comment_base.js +72 -69
  53. package/core/events/events_comment_change.js +63 -61
  54. package/core/events/events_comment_create.js +44 -42
  55. package/core/events/events_comment_delete.js +42 -40
  56. package/core/events/events_comment_move.js +106 -104
  57. package/core/events/events_marker_move.js +65 -64
  58. package/core/events/events_selected.js +46 -45
  59. package/core/events/events_theme_change.js +36 -35
  60. package/core/events/events_toolbox_item_select.js +46 -45
  61. package/core/events/events_trashcan_open.js +37 -36
  62. package/core/events/events_ui.js +47 -46
  63. package/core/events/events_ui_base.js +30 -29
  64. package/core/events/events_var_base.js +37 -36
  65. package/core/events/events_var_create.js +50 -48
  66. package/core/events/events_var_delete.js +50 -48
  67. package/core/events/events_var_rename.js +51 -49
  68. package/core/events/events_viewport.js +66 -65
  69. package/core/events/utils.js +29 -14
  70. package/core/events/workspace_events.js +49 -55
  71. package/core/extensions.js +4 -3
  72. package/core/field.js +1061 -997
  73. package/core/field_angle.js +462 -442
  74. package/core/field_checkbox.js +194 -182
  75. package/core/field_colour.js +519 -505
  76. package/core/field_dropdown.js +617 -598
  77. package/core/field_image.js +229 -220
  78. package/core/field_label.js +102 -91
  79. package/core/field_label_serializable.js +42 -41
  80. package/core/field_multilineinput.js +372 -358
  81. package/core/field_number.js +272 -253
  82. package/core/field_textinput.js +499 -467
  83. package/core/field_variable.js +458 -420
  84. package/core/flyout_base.js +1005 -952
  85. package/core/flyout_button.js +277 -260
  86. package/core/flyout_horizontal.js +304 -302
  87. package/core/flyout_metrics_manager.js +64 -64
  88. package/core/flyout_vertical.js +306 -300
  89. package/core/generator.js +459 -446
  90. package/core/gesture.js +829 -813
  91. package/core/grid.js +166 -163
  92. package/core/icon.js +168 -159
  93. package/core/inject.js +7 -5
  94. package/core/input.js +257 -248
  95. package/core/insertion_marker_manager.js +655 -624
  96. package/core/internal_constants.js +0 -129
  97. package/core/keyboard_nav/ast_node.js +605 -596
  98. package/core/keyboard_nav/basic_cursor.js +166 -165
  99. package/core/keyboard_nav/cursor.js +99 -97
  100. package/core/keyboard_nav/marker.js +83 -79
  101. package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
  102. package/core/marker_manager.js +153 -141
  103. package/core/menu.js +377 -372
  104. package/core/menuitem.js +223 -217
  105. package/core/metrics_manager.js +403 -390
  106. package/core/mutator.js +468 -437
  107. package/core/names.js +229 -188
  108. package/core/options.js +290 -284
  109. package/core/procedures.js +29 -17
  110. package/core/registry.js +19 -16
  111. package/core/rendered_connection.js +482 -463
  112. package/core/renderers/common/block_rendering.js +9 -3
  113. package/core/renderers/common/constants.js +1119 -1112
  114. package/core/renderers/common/debug.js +14 -0
  115. package/core/renderers/common/debugger.js +338 -316
  116. package/core/renderers/common/drawer.js +380 -370
  117. package/core/renderers/common/i_path_object.js +2 -2
  118. package/core/renderers/common/info.js +626 -618
  119. package/core/renderers/common/marker_svg.js +579 -541
  120. package/core/renderers/common/path_object.js +203 -200
  121. package/core/renderers/common/renderer.js +220 -218
  122. package/core/renderers/geras/constants.js +36 -36
  123. package/core/renderers/geras/drawer.js +155 -147
  124. package/core/renderers/geras/highlight_constants.js +244 -238
  125. package/core/renderers/geras/highlighter.js +231 -179
  126. package/core/renderers/geras/info.js +392 -369
  127. package/core/renderers/geras/measurables/inline_input.js +25 -19
  128. package/core/renderers/geras/measurables/statement_input.js +23 -17
  129. package/core/renderers/geras/path_object.js +106 -121
  130. package/core/renderers/geras/renderer.js +96 -98
  131. package/core/renderers/measurables/base.js +30 -18
  132. package/core/renderers/measurables/bottom_row.js +83 -80
  133. package/core/renderers/measurables/connection.js +22 -15
  134. package/core/renderers/measurables/external_value_input.js +35 -22
  135. package/core/renderers/measurables/field.js +35 -20
  136. package/core/renderers/measurables/hat.js +18 -13
  137. package/core/renderers/measurables/icon.js +24 -17
  138. package/core/renderers/measurables/in_row_spacer.js +15 -13
  139. package/core/renderers/measurables/inline_input.js +43 -33
  140. package/core/renderers/measurables/input_connection.js +41 -28
  141. package/core/renderers/measurables/input_row.js +50 -44
  142. package/core/renderers/measurables/jagged_edge.js +14 -12
  143. package/core/renderers/measurables/next_connection.js +16 -14
  144. package/core/renderers/measurables/output_connection.js +26 -20
  145. package/core/renderers/measurables/previous_connection.js +16 -15
  146. package/core/renderers/measurables/round_corner.js +20 -18
  147. package/core/renderers/measurables/row.js +184 -168
  148. package/core/renderers/measurables/spacer_row.js +38 -23
  149. package/core/renderers/measurables/square_corner.js +18 -16
  150. package/core/renderers/measurables/statement_input.js +23 -20
  151. package/core/renderers/measurables/top_row.js +88 -85
  152. package/core/renderers/minimalist/constants.js +8 -7
  153. package/core/renderers/minimalist/drawer.js +11 -10
  154. package/core/renderers/minimalist/info.js +18 -18
  155. package/core/renderers/minimalist/renderer.js +40 -39
  156. package/core/renderers/thrasos/info.js +258 -248
  157. package/core/renderers/thrasos/renderer.js +20 -20
  158. package/core/renderers/zelos/constants.js +898 -873
  159. package/core/renderers/zelos/drawer.js +186 -169
  160. package/core/renderers/zelos/info.js +502 -479
  161. package/core/renderers/zelos/marker_svg.js +129 -115
  162. package/core/renderers/zelos/measurables/bottom_row.js +31 -30
  163. package/core/renderers/zelos/measurables/inputs.js +22 -21
  164. package/core/renderers/zelos/measurables/row_elements.js +14 -13
  165. package/core/renderers/zelos/measurables/top_row.js +34 -33
  166. package/core/renderers/zelos/path_object.js +181 -180
  167. package/core/renderers/zelos/renderer.js +91 -92
  168. package/core/scrollbar.js +759 -713
  169. package/core/scrollbar_pair.js +250 -245
  170. package/core/serialization/blocks.js +19 -9
  171. package/core/serialization/workspaces.js +3 -2
  172. package/core/shortcut_registry.js +286 -277
  173. package/core/sprites.js +31 -0
  174. package/core/theme.js +135 -141
  175. package/core/theme_manager.js +147 -143
  176. package/core/toolbox/category.js +602 -576
  177. package/core/toolbox/collapsible_category.js +226 -227
  178. package/core/toolbox/separator.js +70 -61
  179. package/core/toolbox/toolbox.js +934 -927
  180. package/core/toolbox/toolbox_item.js +115 -99
  181. package/core/tooltip.js +108 -35
  182. package/core/touch.js +8 -3
  183. package/core/touch_gesture.js +254 -251
  184. package/core/trashcan.js +606 -595
  185. package/core/utils/coordinate.js +97 -95
  186. package/core/utils/dom.js +2 -2
  187. package/core/utils/global.js +2 -0
  188. package/core/utils/rect.js +41 -37
  189. package/core/utils/sentinel.js +25 -0
  190. package/core/utils/size.js +30 -27
  191. package/core/utils/svg.js +18 -16
  192. package/core/variable_map.js +325 -341
  193. package/core/variable_model.js +55 -54
  194. package/core/variables.js +9 -2
  195. package/core/variables_dynamic.js +3 -1
  196. package/core/warning.js +126 -120
  197. package/core/widgetdiv.js +4 -4
  198. package/core/workspace.js +685 -664
  199. package/core/workspace_audio.js +124 -118
  200. package/core/workspace_comment.js +308 -298
  201. package/core/workspace_comment_svg.js +1029 -951
  202. package/core/workspace_drag_surface_svg.js +147 -140
  203. package/core/workspace_dragger.js +70 -71
  204. package/core/workspace_svg.js +2322 -2297
  205. package/core/xml.js +30 -20
  206. package/core/zoom_controls.js +431 -439
  207. package/dart_compressed.js +40 -43
  208. package/dart_compressed.js.map +1 -1
  209. package/generators/dart/colour.js +56 -64
  210. package/generators/dart/lists.js +61 -50
  211. package/generators/dart/math.js +160 -148
  212. package/generators/dart/text.js +83 -61
  213. package/generators/javascript/colour.js +37 -34
  214. package/generators/javascript/lists.js +50 -43
  215. package/generators/javascript/math.js +123 -139
  216. package/generators/javascript/text.js +67 -81
  217. package/generators/lua/colour.js +25 -23
  218. package/generators/lua/lists.js +97 -69
  219. package/generators/lua/logic.js +1 -2
  220. package/generators/lua/math.js +182 -144
  221. package/generators/lua/text.js +116 -99
  222. package/generators/php/colour.js +38 -32
  223. package/generators/php/lists.js +109 -89
  224. package/generators/php/math.js +90 -81
  225. package/generators/php/text.js +63 -61
  226. package/generators/python/colour.js +18 -18
  227. package/generators/python/lists.js +38 -30
  228. package/generators/python/loops.js +12 -8
  229. package/generators/python/math.js +104 -106
  230. package/generators/python/text.js +34 -30
  231. package/javascript_compressed.js +37 -39
  232. package/javascript_compressed.js.map +1 -1
  233. package/lua_compressed.js +39 -42
  234. package/lua_compressed.js.map +1 -1
  235. package/msg/az.js +2 -2
  236. package/msg/be.js +4 -4
  237. package/msg/cs.js +15 -15
  238. package/msg/de.js +1 -1
  239. package/msg/diq.js +1 -1
  240. package/msg/eo.js +1 -1
  241. package/msg/es.js +1 -1
  242. package/msg/fa.js +1 -1
  243. package/msg/fr.js +4 -4
  244. package/msg/he.js +1 -1
  245. package/msg/hr.js +2 -2
  246. package/msg/hy.js +2 -2
  247. package/msg/id.js +12 -12
  248. package/msg/inh.js +14 -14
  249. package/msg/ja.js +7 -7
  250. package/msg/lv.js +29 -29
  251. package/msg/pa.js +3 -3
  252. package/msg/smn.js +436 -0
  253. package/msg/te.js +1 -1
  254. package/msg/yue.js +1 -1
  255. package/msg/zh-hans.js +3 -3
  256. package/msg/zh-hant.js +3 -3
  257. package/package.json +7 -6
  258. package/php_compressed.js +38 -42
  259. package/php_compressed.js.map +1 -1
  260. package/python_compressed.js +26 -25
  261. package/python_compressed.js.map +1 -1
  262. package/blocks/all.js +0 -23
@@ -20,12 +20,13 @@ const aria = goog.require('Blockly.utils.aria');
20
20
  const browserEvents = goog.require('Blockly.browserEvents');
21
21
  const colour = goog.require('Blockly.utils.colour');
22
22
  const dom = goog.require('Blockly.utils.dom');
23
+ const dropDownDiv = goog.require('Blockly.dropDownDiv');
23
24
  const fieldRegistry = goog.require('Blockly.fieldRegistry');
24
25
  const idGenerator = goog.require('Blockly.utils.idGenerator');
25
- const object = goog.require('Blockly.utils.object');
26
- const {DropDownDiv} = goog.require('Blockly.DropDownDiv');
27
26
  const {Field} = goog.require('Blockly.Field');
28
27
  const {KeyCodes} = goog.require('Blockly.utils.KeyCodes');
28
+ /* eslint-disable-next-line no-unused-vars */
29
+ const {Sentinel} = goog.requireType('Blockly.utils.Sentinel');
29
30
  const {Size} = goog.require('Blockly.utils.Size');
30
31
  /** @suppress {extraRequire} */
31
32
  goog.require('Blockly.Events.BlockChange');
@@ -33,219 +34,539 @@ goog.require('Blockly.Events.BlockChange');
33
34
 
34
35
  /**
35
36
  * Class for a colour input field.
36
- * @param {string=} opt_value The initial value of the field. Should be in
37
- * '#rrggbb' format. Defaults to the first value in the default colour array.
38
- * @param {Function=} opt_validator A function that is called to validate
39
- * changes to the field's value. Takes in a colour string & returns a
40
- * validated colour string ('#rrggbb' format), or null to abort the
41
- * change.Blockly.
42
- * @param {Object=} opt_config A map of options used to configure the field.
43
- * See the [field creation documentation]{@link
44
- * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour}
45
- * for a list of properties this parameter supports.
46
37
  * @extends {Field}
47
- * @constructor
48
38
  * @alias Blockly.FieldColour
49
39
  */
50
- const FieldColour = function(opt_value, opt_validator, opt_config) {
51
- FieldColour.superClass_.constructor.call(
52
- this, opt_value, opt_validator, opt_config);
40
+ class FieldColour extends Field {
41
+ /**
42
+ * @param {(string|!Sentinel)=} opt_value The initial value of the
43
+ * field. Should be in '#rrggbb' format. Defaults to the first value in
44
+ * the default colour array.
45
+ * Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by
46
+ * subclasses that want to handle configuration and setting the field
47
+ * value after their own constructors have run).
48
+ * @param {Function=} opt_validator A function that is called to validate
49
+ * changes to the field's value. Takes in a colour string & returns a
50
+ * validated colour string ('#rrggbb' format), or null to abort the
51
+ * change.Blockly.
52
+ * @param {Object=} opt_config A map of options used to configure the field.
53
+ * See the [field creation documentation]{@link
54
+ * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour}
55
+ * for a list of properties this parameter supports.
56
+ */
57
+ constructor(opt_value, opt_validator, opt_config) {
58
+ super(Field.SKIP_SETUP);
59
+
60
+ /**
61
+ * The field's colour picker element.
62
+ * @type {?Element}
63
+ * @private
64
+ */
65
+ this.picker_ = null;
66
+
67
+ /**
68
+ * Index of the currently highlighted element.
69
+ * @type {?number}
70
+ * @private
71
+ */
72
+ this.highlightedIndex_ = null;
73
+
74
+ /**
75
+ * Mouse click event data.
76
+ * @type {?browserEvents.Data}
77
+ * @private
78
+ */
79
+ this.onClickWrapper_ = null;
80
+
81
+ /**
82
+ * Mouse move event data.
83
+ * @type {?browserEvents.Data}
84
+ * @private
85
+ */
86
+ this.onMouseMoveWrapper_ = null;
87
+
88
+ /**
89
+ * Mouse enter event data.
90
+ * @type {?browserEvents.Data}
91
+ * @private
92
+ */
93
+ this.onMouseEnterWrapper_ = null;
94
+
95
+ /**
96
+ * Mouse leave event data.
97
+ * @type {?browserEvents.Data}
98
+ * @private
99
+ */
100
+ this.onMouseLeaveWrapper_ = null;
101
+
102
+ /**
103
+ * Key down event data.
104
+ * @type {?browserEvents.Data}
105
+ * @private
106
+ */
107
+ this.onKeyDownWrapper_ = null;
108
+
109
+ /**
110
+ * Serializable fields are saved by the serializer, non-serializable fields
111
+ * are not. Editable fields should also be serializable.
112
+ * @type {boolean}
113
+ */
114
+ this.SERIALIZABLE = true;
115
+
116
+ /**
117
+ * Mouse cursor style when over the hotspot that initiates the editor.
118
+ * @type {string}
119
+ */
120
+ this.CURSOR = 'default';
121
+
122
+ /**
123
+ * Used to tell if the field needs to be rendered the next time the block is
124
+ * rendered. Colour fields are statically sized, and only need to be
125
+ * rendered at initialization.
126
+ * @type {boolean}
127
+ * @protected
128
+ */
129
+ this.isDirty_ = false;
130
+
131
+ /**
132
+ * Array of colours used by this field. If null, use the global list.
133
+ * @type {Array<string>}
134
+ * @private
135
+ */
136
+ this.colours_ = null;
137
+
138
+ /**
139
+ * Array of colour tooltips used by this field. If null, use the global
140
+ * list.
141
+ * @type {Array<string>}
142
+ * @private
143
+ */
144
+ this.titles_ = null;
145
+
146
+ /**
147
+ * Number of colour columns used by this field. If 0, use the global
148
+ * setting. By default use the global constants for columns.
149
+ * @type {number}
150
+ * @private
151
+ */
152
+ this.columns_ = 0;
153
+
154
+ if (opt_value === Field.SKIP_SETUP) return;
155
+ if (opt_config) this.configure_(opt_config);
156
+ this.setValue(opt_value);
157
+ if (opt_validator) this.setValidator(opt_validator);
158
+ }
53
159
 
54
160
  /**
55
- * The field's colour picker element.
56
- * @type {?Element}
57
- * @private
161
+ * Configure the field based on the given map of options.
162
+ * @param {!Object} config A map of options to configure the field based on.
163
+ * @protected
164
+ * @override
58
165
  */
59
- this.picker_ = null;
166
+ configure_(config) {
167
+ super.configure_(config);
168
+ if (config['colourOptions']) {
169
+ this.colours_ = config['colourOptions'];
170
+ this.titles_ = config['colourTitles'];
171
+ }
172
+ if (config['columns']) {
173
+ this.columns_ = config['columns'];
174
+ }
175
+ }
60
176
 
61
177
  /**
62
- * Index of the currently highlighted element.
63
- * @type {?number}
64
- * @private
178
+ * Create the block UI for this colour field.
179
+ * @package
65
180
  */
66
- this.highlightedIndex_ = null;
181
+ initView() {
182
+ this.size_ = new Size(
183
+ this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH,
184
+ this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT);
185
+ if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) {
186
+ this.createBorderRect_();
187
+ this.borderRect_.style['fillOpacity'] = '1';
188
+ } else {
189
+ this.clickTarget_ = this.sourceBlock_.getSvgRoot();
190
+ }
191
+ }
67
192
 
68
193
  /**
69
- * Mouse click event data.
70
- * @type {?browserEvents.Data}
71
- * @private
194
+ * @override
72
195
  */
73
- this.onClickWrapper_ = null;
196
+ applyColour() {
197
+ if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) {
198
+ if (this.borderRect_) {
199
+ this.borderRect_.style.fill = /** @type {string} */ (this.getValue());
200
+ }
201
+ } else {
202
+ this.sourceBlock_.pathObject.svgPath.setAttribute(
203
+ 'fill', this.getValue());
204
+ this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
205
+ }
206
+ }
74
207
 
75
208
  /**
76
- * Mouse move event data.
77
- * @type {?browserEvents.Data}
78
- * @private
209
+ * Ensure that the input value is a valid colour.
210
+ * @param {*=} opt_newValue The input value.
211
+ * @return {?string} A valid colour, or null if invalid.
212
+ * @protected
79
213
  */
80
- this.onMouseMoveWrapper_ = null;
214
+ doClassValidation_(opt_newValue) {
215
+ if (typeof opt_newValue !== 'string') {
216
+ return null;
217
+ }
218
+ return colour.parse(opt_newValue);
219
+ }
81
220
 
82
221
  /**
83
- * Mouse enter event data.
84
- * @type {?browserEvents.Data}
85
- * @private
222
+ * Update the value of this colour field, and update the displayed colour.
223
+ * @param {*} newValue The value to be saved. The default validator guarantees
224
+ * that this is a colour in '#rrggbb' format.
225
+ * @protected
86
226
  */
87
- this.onMouseEnterWrapper_ = null;
227
+ doValueUpdate_(newValue) {
228
+ this.value_ = newValue;
229
+ if (this.borderRect_) {
230
+ this.borderRect_.style.fill = /** @type {string} */ (newValue);
231
+ } else if (this.sourceBlock_ && this.sourceBlock_.rendered) {
232
+ this.sourceBlock_.pathObject.svgPath.setAttribute('fill', newValue);
233
+ this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
234
+ }
235
+ }
88
236
 
89
237
  /**
90
- * Mouse leave event data.
91
- * @type {?browserEvents.Data}
92
- * @private
238
+ * Get the text for this field. Used when the block is collapsed.
239
+ * @return {string} Text representing the value of this field.
93
240
  */
94
- this.onMouseLeaveWrapper_ = null;
241
+ getText() {
242
+ let colour = /** @type {string} */ (this.value_);
243
+ // Try to use #rgb format if possible, rather than #rrggbb.
244
+ if (/^#(.)\1(.)\2(.)\3$/.test(colour)) {
245
+ colour = '#' + colour[1] + colour[3] + colour[5];
246
+ }
247
+ return colour;
248
+ }
95
249
 
96
250
  /**
97
- * Key down event data.
98
- * @type {?browserEvents.Data}
99
- * @private
251
+ * Set a custom colour grid for this field.
252
+ * @param {Array<string>} colours Array of colours for this block,
253
+ * or null to use default (FieldColour.COLOURS).
254
+ * @param {Array<string>=} opt_titles Optional array of colour tooltips,
255
+ * or null to use default (FieldColour.TITLES).
256
+ * @return {!FieldColour} Returns itself (for method chaining).
100
257
  */
101
- this.onKeyDownWrapper_ = null;
102
- };
103
- object.inherits(FieldColour, Field);
258
+ setColours(colours, opt_titles) {
259
+ this.colours_ = colours;
260
+ if (opt_titles) {
261
+ this.titles_ = opt_titles;
262
+ }
263
+ return this;
264
+ }
104
265
 
105
- /**
106
- * Construct a FieldColour from a JSON arg object.
107
- * @param {!Object} options A JSON object with options (colour).
108
- * @return {!FieldColour} The new field instance.
109
- * @package
110
- * @nocollapse
111
- */
112
- FieldColour.fromJson = function(options) {
113
- // `this` might be a subclass of FieldColour if that class doesn't override
114
- // the static fromJson method.
115
- return new this(options['colour'], undefined, options);
116
- };
266
+ /**
267
+ * Set a custom grid size for this field.
268
+ * @param {number} columns Number of columns for this block,
269
+ * or 0 to use default (FieldColour.COLUMNS).
270
+ * @return {!FieldColour} Returns itself (for method chaining).
271
+ */
272
+ setColumns(columns) {
273
+ this.columns_ = columns;
274
+ return this;
275
+ }
117
276
 
118
- /**
119
- * Serializable fields are saved by the XML renderer, non-serializable fields
120
- * are not. Editable fields should also be serializable.
121
- * @type {boolean}
122
- */
123
- FieldColour.prototype.SERIALIZABLE = true;
277
+ /**
278
+ * Create and show the colour field's editor.
279
+ * @protected
280
+ */
281
+ showEditor_() {
282
+ this.dropdownCreate_();
283
+ dropDownDiv.getContentDiv().appendChild(this.picker_);
124
284
 
125
- /**
126
- * Mouse cursor style when over the hotspot that initiates the editor.
127
- */
128
- FieldColour.prototype.CURSOR = 'default';
285
+ dropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
129
286
 
130
- /**
131
- * Used to tell if the field needs to be rendered the next time the block is
132
- * rendered. Colour fields are statically sized, and only need to be
133
- * rendered at initialization.
134
- * @type {boolean}
135
- * @protected
136
- */
137
- FieldColour.prototype.isDirty_ = false;
287
+ // Focus so we can start receiving keyboard events.
288
+ this.picker_.focus({preventScroll: true});
289
+ }
138
290
 
139
- /**
140
- * Array of colours used by this field. If null, use the global list.
141
- * @type {Array<string>}
142
- * @private
143
- */
144
- FieldColour.prototype.colours_ = null;
291
+ /**
292
+ * Handle a click on a colour cell.
293
+ * @param {!MouseEvent} e Mouse event.
294
+ * @private
295
+ */
296
+ onClick_(e) {
297
+ const cell = /** @type {!Element} */ (e.target);
298
+ const colour = cell && cell.label;
299
+ if (colour !== null) {
300
+ this.setValue(colour);
301
+ dropDownDiv.hideIfOwner(this);
302
+ }
303
+ }
145
304
 
146
- /**
147
- * Array of colour tooltips used by this field. If null, use the global list.
148
- * @type {Array<string>}
149
- * @private
150
- */
151
- FieldColour.prototype.titles_ = null;
305
+ /**
306
+ * Handle a key down event. Navigate around the grid with the
307
+ * arrow keys. Enter selects the highlighted colour.
308
+ * @param {!KeyboardEvent} e Keyboard event.
309
+ * @private
310
+ */
311
+ onKeyDown_(e) {
312
+ let handled = false;
313
+ if (e.keyCode === KeyCodes.UP) {
314
+ this.moveHighlightBy_(0, -1);
315
+ handled = true;
316
+ } else if (e.keyCode === KeyCodes.DOWN) {
317
+ this.moveHighlightBy_(0, 1);
318
+ handled = true;
319
+ } else if (e.keyCode === KeyCodes.LEFT) {
320
+ this.moveHighlightBy_(-1, 0);
321
+ handled = true;
322
+ } else if (e.keyCode === KeyCodes.RIGHT) {
323
+ this.moveHighlightBy_(1, 0);
324
+ handled = true;
325
+ } else if (e.keyCode === KeyCodes.ENTER) {
326
+ // Select the highlighted colour.
327
+ const highlighted = this.getHighlighted_();
328
+ if (highlighted) {
329
+ const colour = highlighted && highlighted.label;
330
+ if (colour !== null) {
331
+ this.setValue(colour);
332
+ }
333
+ }
334
+ dropDownDiv.hideWithoutAnimation();
335
+ handled = true;
336
+ }
337
+ if (handled) {
338
+ e.stopPropagation();
339
+ }
340
+ }
152
341
 
153
- /**
154
- * Number of colour columns used by this field. If 0, use the global setting.
155
- * By default use the global constants for columns.
156
- * @type {number}
157
- * @private
158
- */
159
- FieldColour.prototype.columns_ = 0;
342
+ /**
343
+ * Move the currently highlighted position by dx and dy.
344
+ * @param {number} dx Change of x
345
+ * @param {number} dy Change of y
346
+ * @private
347
+ */
348
+ moveHighlightBy_(dx, dy) {
349
+ const colours = this.colours_ || FieldColour.COLOURS;
350
+ const columns = this.columns_ || FieldColour.COLUMNS;
351
+
352
+ // Get the current x and y coordinates
353
+ let x = this.highlightedIndex_ % columns;
354
+ let y = Math.floor(this.highlightedIndex_ / columns);
355
+
356
+ // Add the offset
357
+ x += dx;
358
+ y += dy;
359
+
360
+ if (dx < 0) {
361
+ // Move left one grid cell, even in RTL.
362
+ // Loop back to the end of the previous row if we have room.
363
+ if (x < 0 && y > 0) {
364
+ x = columns - 1;
365
+ y--;
366
+ } else if (x < 0) {
367
+ x = 0;
368
+ }
369
+ } else if (dx > 0) {
370
+ // Move right one grid cell, even in RTL.
371
+ // Loop to the start of the next row, if there's room.
372
+ if (x > columns - 1 && y < Math.floor(colours.length / columns) - 1) {
373
+ x = 0;
374
+ y++;
375
+ } else if (x > columns - 1) {
376
+ x--;
377
+ }
378
+ } else if (dy < 0) {
379
+ // Move up one grid cell, stop at the top.
380
+ if (y < 0) {
381
+ y = 0;
382
+ }
383
+ } else if (dy > 0) {
384
+ // Move down one grid cell, stop at the bottom.
385
+ if (y > Math.floor(colours.length / columns) - 1) {
386
+ y = Math.floor(colours.length / columns) - 1;
387
+ }
388
+ }
160
389
 
161
- /**
162
- * Configure the field based on the given map of options.
163
- * @param {!Object} config A map of options to configure the field based on.
164
- * @protected
165
- * @override
166
- */
167
- FieldColour.prototype.configure_ = function(config) {
168
- FieldColour.superClass_.configure_.call(this, config);
169
- if (config['colourOptions']) {
170
- this.colours_ = config['colourOptions'];
171
- this.titles_ = config['colourTitles'];
390
+ // Move the highlight to the new coordinates.
391
+ const cell =
392
+ /** @type {!Element} */ (this.picker_.childNodes[y].childNodes[x]);
393
+ const index = (y * columns) + x;
394
+ this.setHighlightedCell_(cell, index);
172
395
  }
173
- if (config['columns']) {
174
- this.columns_ = config['columns'];
396
+
397
+ /**
398
+ * Handle a mouse move event. Highlight the hovered colour.
399
+ * @param {!MouseEvent} e Mouse event.
400
+ * @private
401
+ */
402
+ onMouseMove_(e) {
403
+ const cell = /** @type {!Element} */ (e.target);
404
+ const index = cell && Number(cell.getAttribute('data-index'));
405
+ if (index !== null && index !== this.highlightedIndex_) {
406
+ this.setHighlightedCell_(cell, index);
407
+ }
175
408
  }
176
- };
177
409
 
178
- /**
179
- * Create the block UI for this colour field.
180
- * @package
181
- */
182
- FieldColour.prototype.initView = function() {
183
- this.size_ = new Size(
184
- this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH,
185
- this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT);
186
- if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) {
187
- this.createBorderRect_();
188
- this.borderRect_.style['fillOpacity'] = '1';
189
- } else {
190
- this.clickTarget_ = this.sourceBlock_.getSvgRoot();
410
+ /**
411
+ * Handle a mouse enter event. Focus the picker.
412
+ * @private
413
+ */
414
+ onMouseEnter_() {
415
+ this.picker_.focus({preventScroll: true});
191
416
  }
192
- };
193
417
 
194
- /**
195
- * @override
196
- */
197
- FieldColour.prototype.applyColour = function() {
198
- if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) {
199
- if (this.borderRect_) {
200
- this.borderRect_.style.fill = /** @type {string} */ (this.getValue());
418
+ /**
419
+ * Handle a mouse leave event. Blur the picker and unhighlight
420
+ * the currently highlighted colour.
421
+ * @private
422
+ */
423
+ onMouseLeave_() {
424
+ this.picker_.blur();
425
+ const highlighted = this.getHighlighted_();
426
+ if (highlighted) {
427
+ dom.removeClass(highlighted, 'blocklyColourHighlighted');
201
428
  }
202
- } else {
203
- this.sourceBlock_.pathObject.svgPath.setAttribute('fill', this.getValue());
204
- this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
205
429
  }
206
- };
207
430
 
208
- /**
209
- * Ensure that the input value is a valid colour.
210
- * @param {*=} opt_newValue The input value.
211
- * @return {?string} A valid colour, or null if invalid.
212
- * @protected
213
- */
214
- FieldColour.prototype.doClassValidation_ = function(opt_newValue) {
215
- if (typeof opt_newValue !== 'string') {
216
- return null;
431
+ /**
432
+ * Returns the currently highlighted item (if any).
433
+ * @return {?HTMLElement} Highlighted item (null if none).
434
+ * @private
435
+ */
436
+ getHighlighted_() {
437
+ const columns = this.columns_ || FieldColour.COLUMNS;
438
+ const x = this.highlightedIndex_ % columns;
439
+ const y = Math.floor(this.highlightedIndex_ / columns);
440
+ const row = this.picker_.childNodes[y];
441
+ if (!row) {
442
+ return null;
443
+ }
444
+ const col = /** @type {HTMLElement} */ (row.childNodes[x]);
445
+ return col;
217
446
  }
218
- return colour.parse(opt_newValue);
219
- };
220
447
 
221
- /**
222
- * Update the value of this colour field, and update the displayed colour.
223
- * @param {*} newValue The value to be saved. The default validator guarantees
224
- * that this is a colour in '#rrggbb' format.
225
- * @protected
226
- */
227
- FieldColour.prototype.doValueUpdate_ = function(newValue) {
228
- this.value_ = newValue;
229
- if (this.borderRect_) {
230
- this.borderRect_.style.fill = /** @type {string} */ (newValue);
231
- } else if (this.sourceBlock_ && this.sourceBlock_.rendered) {
232
- this.sourceBlock_.pathObject.svgPath.setAttribute('fill', newValue);
233
- this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff');
448
+ /**
449
+ * Update the currently highlighted cell.
450
+ * @param {!Element} cell the new cell to highlight
451
+ * @param {number} index the index of the new cell
452
+ * @private
453
+ */
454
+ setHighlightedCell_(cell, index) {
455
+ // Unhighlight the current item.
456
+ const highlighted = this.getHighlighted_();
457
+ if (highlighted) {
458
+ dom.removeClass(highlighted, 'blocklyColourHighlighted');
459
+ }
460
+ // Highlight new item.
461
+ dom.addClass(cell, 'blocklyColourHighlighted');
462
+ // Set new highlighted index.
463
+ this.highlightedIndex_ = index;
464
+
465
+ // Update accessibility roles.
466
+ aria.setState(
467
+ /** @type {!Element} */ (this.picker_), aria.State.ACTIVEDESCENDANT,
468
+ cell.getAttribute('id'));
234
469
  }
235
- };
236
470
 
237
- /**
238
- * Get the text for this field. Used when the block is collapsed.
239
- * @return {string} Text representing the value of this field.
240
- */
241
- FieldColour.prototype.getText = function() {
242
- let colour = /** @type {string} */ (this.value_);
243
- // Try to use #rgb format if possible, rather than #rrggbb.
244
- if (/^#(.)\1(.)\2(.)\3$/.test(colour)) {
245
- colour = '#' + colour[1] + colour[3] + colour[5];
471
+ /**
472
+ * Create a colour picker dropdown editor.
473
+ * @private
474
+ */
475
+ dropdownCreate_() {
476
+ const columns = this.columns_ || FieldColour.COLUMNS;
477
+ const colours = this.colours_ || FieldColour.COLOURS;
478
+ const titles = this.titles_ || FieldColour.TITLES;
479
+ const selectedColour = this.getValue();
480
+ // Create the palette.
481
+ const table = document.createElement('table');
482
+ table.className = 'blocklyColourTable';
483
+ table.tabIndex = 0;
484
+ table.dir = 'ltr';
485
+ aria.setRole(table, aria.Role.GRID);
486
+ aria.setState(table, aria.State.EXPANDED, true);
487
+ aria.setState(
488
+ table, aria.State.ROWCOUNT, Math.floor(colours.length / columns));
489
+ aria.setState(table, aria.State.COLCOUNT, columns);
490
+ let row;
491
+ for (let i = 0; i < colours.length; i++) {
492
+ if (i % columns === 0) {
493
+ row = document.createElement('tr');
494
+ aria.setRole(row, aria.Role.ROW);
495
+ table.appendChild(row);
496
+ }
497
+ const cell = document.createElement('td');
498
+ row.appendChild(cell);
499
+ cell.label = colours[i]; // This becomes the value, if clicked.
500
+ cell.title = titles[i] || colours[i];
501
+ cell.id = idGenerator.getNextUniqueId();
502
+ cell.setAttribute('data-index', i);
503
+ aria.setRole(cell, aria.Role.GRIDCELL);
504
+ aria.setState(cell, aria.State.LABEL, colours[i]);
505
+ aria.setState(cell, aria.State.SELECTED, colours[i] === selectedColour);
506
+ cell.style.backgroundColor = colours[i];
507
+ if (colours[i] === selectedColour) {
508
+ cell.className = 'blocklyColourSelected';
509
+ this.highlightedIndex_ = i;
510
+ }
511
+ }
512
+
513
+ // Configure event handler on the table to listen for any event in a cell.
514
+ this.onClickWrapper_ = browserEvents.conditionalBind(
515
+ table, 'click', this, this.onClick_, true);
516
+ this.onMouseMoveWrapper_ = browserEvents.conditionalBind(
517
+ table, 'mousemove', this, this.onMouseMove_, true);
518
+ this.onMouseEnterWrapper_ = browserEvents.conditionalBind(
519
+ table, 'mouseenter', this, this.onMouseEnter_, true);
520
+ this.onMouseLeaveWrapper_ = browserEvents.conditionalBind(
521
+ table, 'mouseleave', this, this.onMouseLeave_, true);
522
+ this.onKeyDownWrapper_ =
523
+ browserEvents.conditionalBind(table, 'keydown', this, this.onKeyDown_);
524
+
525
+ this.picker_ = table;
526
+ }
527
+
528
+ /**
529
+ * Disposes of events and DOM-references belonging to the colour editor.
530
+ * @private
531
+ */
532
+ dropdownDispose_() {
533
+ if (this.onClickWrapper_) {
534
+ browserEvents.unbind(this.onClickWrapper_);
535
+ this.onClickWrapper_ = null;
536
+ }
537
+ if (this.onMouseMoveWrapper_) {
538
+ browserEvents.unbind(this.onMouseMoveWrapper_);
539
+ this.onMouseMoveWrapper_ = null;
540
+ }
541
+ if (this.onMouseEnterWrapper_) {
542
+ browserEvents.unbind(this.onMouseEnterWrapper_);
543
+ this.onMouseEnterWrapper_ = null;
544
+ }
545
+ if (this.onMouseLeaveWrapper_) {
546
+ browserEvents.unbind(this.onMouseLeaveWrapper_);
547
+ this.onMouseLeaveWrapper_ = null;
548
+ }
549
+ if (this.onKeyDownWrapper_) {
550
+ browserEvents.unbind(this.onKeyDownWrapper_);
551
+ this.onKeyDownWrapper_ = null;
552
+ }
553
+ this.picker_ = null;
554
+ this.highlightedIndex_ = null;
555
+ }
556
+
557
+ /**
558
+ * Construct a FieldColour from a JSON arg object.
559
+ * @param {!Object} options A JSON object with options (colour).
560
+ * @return {!FieldColour} The new field instance.
561
+ * @package
562
+ * @nocollapse
563
+ */
564
+ static fromJson(options) {
565
+ // `this` might be a subclass of FieldColour if that class doesn't override
566
+ // the static fromJson method.
567
+ return new this(options['colour'], undefined, options);
246
568
  }
247
- return colour;
248
- };
569
+ }
249
570
 
250
571
  /**
251
572
  * An array of colour strings for the palette.
@@ -357,345 +678,38 @@ FieldColour.TITLES = [];
357
678
  */
358
679
  FieldColour.COLUMNS = 7;
359
680
 
360
- /**
361
- * Set a custom colour grid for this field.
362
- * @param {Array<string>} colours Array of colours for this block,
363
- * or null to use default (FieldColour.COLOURS).
364
- * @param {Array<string>=} opt_titles Optional array of colour tooltips,
365
- * or null to use default (FieldColour.TITLES).
366
- * @return {!FieldColour} Returns itself (for method chaining).
367
- */
368
- FieldColour.prototype.setColours = function(colours, opt_titles) {
369
- this.colours_ = colours;
370
- if (opt_titles) {
371
- this.titles_ = opt_titles;
372
- }
373
- return this;
374
- };
375
-
376
- /**
377
- * Set a custom grid size for this field.
378
- * @param {number} columns Number of columns for this block,
379
- * or 0 to use default (FieldColour.COLUMNS).
380
- * @return {!FieldColour} Returns itself (for method chaining).
381
- */
382
- FieldColour.prototype.setColumns = function(columns) {
383
- this.columns_ = columns;
384
- return this;
385
- };
386
-
387
- /**
388
- * Create and show the colour field's editor.
389
- * @protected
390
- */
391
- FieldColour.prototype.showEditor_ = function() {
392
- this.dropdownCreate_();
393
- DropDownDiv.getContentDiv().appendChild(this.picker_);
394
-
395
- DropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
396
-
397
- // Focus so we can start receiving keyboard events.
398
- this.picker_.focus({preventScroll: true});
399
- };
400
-
401
- /**
402
- * Handle a click on a colour cell.
403
- * @param {!MouseEvent} e Mouse event.
404
- * @private
405
- */
406
- FieldColour.prototype.onClick_ = function(e) {
407
- const cell = /** @type {!Element} */ (e.target);
408
- const colour = cell && cell.label;
409
- if (colour !== null) {
410
- this.setValue(colour);
411
- DropDownDiv.hideIfOwner(this);
412
- }
413
- };
414
-
415
- /**
416
- * Handle a key down event. Navigate around the grid with the
417
- * arrow keys. Enter selects the highlighted colour.
418
- * @param {!KeyboardEvent} e Keyboard event.
419
- * @private
420
- */
421
- FieldColour.prototype.onKeyDown_ = function(e) {
422
- let handled = false;
423
- if (e.keyCode === KeyCodes.UP) {
424
- this.moveHighlightBy_(0, -1);
425
- handled = true;
426
- } else if (e.keyCode === KeyCodes.DOWN) {
427
- this.moveHighlightBy_(0, 1);
428
- handled = true;
429
- } else if (e.keyCode === KeyCodes.LEFT) {
430
- this.moveHighlightBy_(-1, 0);
431
- handled = true;
432
- } else if (e.keyCode === KeyCodes.RIGHT) {
433
- this.moveHighlightBy_(1, 0);
434
- handled = true;
435
- } else if (e.keyCode === KeyCodes.ENTER) {
436
- // Select the highlighted colour.
437
- const highlighted = this.getHighlighted_();
438
- if (highlighted) {
439
- const colour = highlighted && highlighted.label;
440
- if (colour !== null) {
441
- this.setValue(colour);
442
- }
443
- }
444
- DropDownDiv.hideWithoutAnimation();
445
- handled = true;
446
- }
447
- if (handled) {
448
- e.stopPropagation();
449
- }
450
- };
451
-
452
- /**
453
- * Move the currently highlighted position by dx and dy.
454
- * @param {number} dx Change of x
455
- * @param {number} dy Change of y
456
- * @private
457
- */
458
- FieldColour.prototype.moveHighlightBy_ = function(dx, dy) {
459
- const colours = this.colours_ || FieldColour.COLOURS;
460
- const columns = this.columns_ || FieldColour.COLUMNS;
461
-
462
- // Get the current x and y coordinates
463
- let x = this.highlightedIndex_ % columns;
464
- let y = Math.floor(this.highlightedIndex_ / columns);
465
-
466
- // Add the offset
467
- x += dx;
468
- y += dy;
469
-
470
- if (dx < 0) {
471
- // Move left one grid cell, even in RTL.
472
- // Loop back to the end of the previous row if we have room.
473
- if (x < 0 && y > 0) {
474
- x = columns - 1;
475
- y--;
476
- } else if (x < 0) {
477
- x = 0;
478
- }
479
- } else if (dx > 0) {
480
- // Move right one grid cell, even in RTL.
481
- // Loop to the start of the next row, if there's room.
482
- if (x > columns - 1 && y < Math.floor(colours.length / columns) - 1) {
483
- x = 0;
484
- y++;
485
- } else if (x > columns - 1) {
486
- x--;
487
- }
488
- } else if (dy < 0) {
489
- // Move up one grid cell, stop at the top.
490
- if (y < 0) {
491
- y = 0;
492
- }
493
- } else if (dy > 0) {
494
- // Move down one grid cell, stop at the bottom.
495
- if (y > Math.floor(colours.length / columns) - 1) {
496
- y = Math.floor(colours.length / columns) - 1;
497
- }
498
- }
499
-
500
- // Move the highlight to the new coordinates.
501
- const cell =
502
- /** @type {!Element} */ (this.picker_.childNodes[y].childNodes[x]);
503
- const index = (y * columns) + x;
504
- this.setHighlightedCell_(cell, index);
505
- };
506
-
507
- /**
508
- * Handle a mouse move event. Highlight the hovered colour.
509
- * @param {!MouseEvent} e Mouse event.
510
- * @private
511
- */
512
- FieldColour.prototype.onMouseMove_ = function(e) {
513
- const cell = /** @type {!Element} */ (e.target);
514
- const index = cell && Number(cell.getAttribute('data-index'));
515
- if (index !== null && index !== this.highlightedIndex_) {
516
- this.setHighlightedCell_(cell, index);
517
- }
518
- };
519
-
520
- /**
521
- * Handle a mouse enter event. Focus the picker.
522
- * @private
523
- */
524
- FieldColour.prototype.onMouseEnter_ = function() {
525
- this.picker_.focus({preventScroll: true});
526
- };
527
-
528
- /**
529
- * Handle a mouse leave event. Blur the picker and unhighlight
530
- * the currently highlighted colour.
531
- * @private
532
- */
533
- FieldColour.prototype.onMouseLeave_ = function() {
534
- this.picker_.blur();
535
- const highlighted = this.getHighlighted_();
536
- if (highlighted) {
537
- dom.removeClass(highlighted, 'blocklyColourHighlighted');
538
- }
539
- };
540
-
541
- /**
542
- * Returns the currently highlighted item (if any).
543
- * @return {?HTMLElement} Highlighted item (null if none).
544
- * @private
545
- */
546
- FieldColour.prototype.getHighlighted_ = function() {
547
- const columns = this.columns_ || FieldColour.COLUMNS;
548
- const x = this.highlightedIndex_ % columns;
549
- const y = Math.floor(this.highlightedIndex_ / columns);
550
- const row = this.picker_.childNodes[y];
551
- if (!row) {
552
- return null;
553
- }
554
- const col = /** @type {HTMLElement} */ (row.childNodes[x]);
555
- return col;
556
- };
557
-
558
- /**
559
- * Update the currently highlighted cell.
560
- * @param {!Element} cell the new cell to highlight
561
- * @param {number} index the index of the new cell
562
- * @private
563
- */
564
- FieldColour.prototype.setHighlightedCell_ = function(cell, index) {
565
- // Unhighlight the current item.
566
- const highlighted = this.getHighlighted_();
567
- if (highlighted) {
568
- dom.removeClass(highlighted, 'blocklyColourHighlighted');
569
- }
570
- // Highlight new item.
571
- dom.addClass(cell, 'blocklyColourHighlighted');
572
- // Set new highlighted index.
573
- this.highlightedIndex_ = index;
574
-
575
- // Update accessibility roles.
576
- aria.setState(
577
- /** @type {!Element} */ (this.picker_), aria.State.ACTIVEDESCENDANT,
578
- cell.getAttribute('id'));
579
- };
580
-
581
- /**
582
- * Create a colour picker dropdown editor.
583
- * @private
584
- */
585
- FieldColour.prototype.dropdownCreate_ = function() {
586
- const columns = this.columns_ || FieldColour.COLUMNS;
587
- const colours = this.colours_ || FieldColour.COLOURS;
588
- const titles = this.titles_ || FieldColour.TITLES;
589
- const selectedColour = this.getValue();
590
- // Create the palette.
591
- const table = document.createElement('table');
592
- table.className = 'blocklyColourTable';
593
- table.tabIndex = 0;
594
- table.dir = 'ltr';
595
- aria.setRole(table, aria.Role.GRID);
596
- aria.setState(table, aria.State.EXPANDED, true);
597
- aria.setState(
598
- table, aria.State.ROWCOUNT, Math.floor(colours.length / columns));
599
- aria.setState(table, aria.State.COLCOUNT, columns);
600
- let row;
601
- for (let i = 0; i < colours.length; i++) {
602
- if (i % columns === 0) {
603
- row = document.createElement('tr');
604
- aria.setRole(row, aria.Role.ROW);
605
- table.appendChild(row);
606
- }
607
- const cell = document.createElement('td');
608
- row.appendChild(cell);
609
- cell.label = colours[i]; // This becomes the value, if clicked.
610
- cell.title = titles[i] || colours[i];
611
- cell.id = idGenerator.getNextUniqueId();
612
- cell.setAttribute('data-index', i);
613
- aria.setRole(cell, aria.Role.GRIDCELL);
614
- aria.setState(cell, aria.State.LABEL, colours[i]);
615
- aria.setState(cell, aria.State.SELECTED, colours[i] === selectedColour);
616
- cell.style.backgroundColor = colours[i];
617
- if (colours[i] === selectedColour) {
618
- cell.className = 'blocklyColourSelected';
619
- this.highlightedIndex_ = i;
620
- }
621
- }
622
-
623
- // Configure event handler on the table to listen for any event in a cell.
624
- this.onClickWrapper_ =
625
- browserEvents.conditionalBind(table, 'click', this, this.onClick_, true);
626
- this.onMouseMoveWrapper_ = browserEvents.conditionalBind(
627
- table, 'mousemove', this, this.onMouseMove_, true);
628
- this.onMouseEnterWrapper_ = browserEvents.conditionalBind(
629
- table, 'mouseenter', this, this.onMouseEnter_, true);
630
- this.onMouseLeaveWrapper_ = browserEvents.conditionalBind(
631
- table, 'mouseleave', this, this.onMouseLeave_, true);
632
- this.onKeyDownWrapper_ =
633
- browserEvents.conditionalBind(table, 'keydown', this, this.onKeyDown_);
634
-
635
- this.picker_ = table;
636
- };
637
-
638
- /**
639
- * Disposes of events and DOM-references belonging to the colour editor.
640
- * @private
641
- */
642
- FieldColour.prototype.dropdownDispose_ = function() {
643
- if (this.onClickWrapper_) {
644
- browserEvents.unbind(this.onClickWrapper_);
645
- this.onClickWrapper_ = null;
646
- }
647
- if (this.onMouseMoveWrapper_) {
648
- browserEvents.unbind(this.onMouseMoveWrapper_);
649
- this.onMouseMoveWrapper_ = null;
650
- }
651
- if (this.onMouseEnterWrapper_) {
652
- browserEvents.unbind(this.onMouseEnterWrapper_);
653
- this.onMouseEnterWrapper_ = null;
654
- }
655
- if (this.onMouseLeaveWrapper_) {
656
- browserEvents.unbind(this.onMouseLeaveWrapper_);
657
- this.onMouseLeaveWrapper_ = null;
658
- }
659
- if (this.onKeyDownWrapper_) {
660
- browserEvents.unbind(this.onKeyDownWrapper_);
661
- this.onKeyDownWrapper_ = null;
662
- }
663
- this.picker_ = null;
664
- this.highlightedIndex_ = null;
665
- };
666
-
667
681
  /**
668
682
  * CSS for colour picker. See css.js for use.
669
683
  */
670
684
  Css.register(`
671
- .blocklyColourTable {
672
- border-collapse: collapse;
673
- display: block;
674
- outline: none;
675
- padding: 1px;
676
- }
677
-
678
- .blocklyColourTable>tr>td {
679
- border: .5px solid #888;
680
- box-sizing: border-box;
681
- cursor: pointer;
682
- display: inline-block;
683
- height: 20px;
684
- padding: 0;
685
- width: 20px;
686
- }
687
-
688
- .blocklyColourTable>tr>td.blocklyColourHighlighted {
689
- border-color: #eee;
690
- box-shadow: 2px 2px 7px 2px rgba(0,0,0,.3);
691
- position: relative;
692
- }
693
-
694
- .blocklyColourSelected, .blocklyColourSelected:hover {
695
- border-color: #eee !important;
696
- outline: 1px solid #333;
697
- position: relative;
698
- }
685
+ .blocklyColourTable {
686
+ border-collapse: collapse;
687
+ display: block;
688
+ outline: none;
689
+ padding: 1px;
690
+ }
691
+
692
+ .blocklyColourTable>tr>td {
693
+ border: .5px solid #888;
694
+ box-sizing: border-box;
695
+ cursor: pointer;
696
+ display: inline-block;
697
+ height: 20px;
698
+ padding: 0;
699
+ width: 20px;
700
+ }
701
+
702
+ .blocklyColourTable>tr>td.blocklyColourHighlighted {
703
+ border-color: #eee;
704
+ box-shadow: 2px 2px 7px 2px rgba(0,0,0,.3);
705
+ position: relative;
706
+ }
707
+
708
+ .blocklyColourSelected, .blocklyColourSelected:hover {
709
+ border-color: #eee !important;
710
+ outline: 1px solid #333;
711
+ position: relative;
712
+ }
699
713
  `);
700
714
 
701
715
  fieldRegistry.register('field_colour', FieldColour);