blockly 7.20211209.4 → 8.0.1
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/blockly.d.ts +18963 -18432
- package/blockly.min.js +5 -4
- package/blockly_compressed.js +4 -3
- package/blockly_compressed.js.map +1 -1
- package/blocks/blocks.js +47 -0
- package/blocks/colour.js +13 -3
- package/blocks/lists.js +22 -13
- package/blocks/logic.js +13 -3
- package/blocks/loops.js +24 -11
- package/blocks/math.js +12 -3
- package/blocks/procedures.js +45 -32
- package/blocks/text.js +22 -13
- package/blocks/variables.js +14 -3
- package/blocks/variables_dynamic.js +13 -3
- package/blocks_compressed.js +1 -1
- package/blocks_compressed.js.map +1 -1
- package/core/block.js +1869 -1814
- package/core/block_drag_surface.js +201 -200
- package/core/block_dragger.js +377 -373
- package/core/block_svg.js +1593 -1479
- package/core/blockly.js +8 -22
- package/core/blocks.js +9 -2
- package/core/browser_events.js +22 -5
- package/core/bubble.js +841 -797
- package/core/bubble_dragger.js +213 -206
- package/core/bump_objects.js +2 -2
- package/core/clipboard.js +9 -9
- package/core/comment.js +353 -332
- package/core/common.js +46 -17
- package/core/component_manager.js +181 -174
- package/core/config.js +87 -0
- package/core/connection.js +595 -584
- package/core/connection_checker.js +242 -244
- package/core/connection_db.js +235 -230
- package/core/contextmenu.js +9 -6
- package/core/contextmenu_items.js +1 -2
- package/core/contextmenu_registry.js +93 -89
- package/core/css.js +474 -474
- package/core/delete_area.js +45 -42
- package/core/drag_target.js +57 -56
- package/core/dropdowndiv.js +153 -163
- package/core/events/events.js +2 -2
- package/core/events/events_abstract.js +89 -77
- package/core/events/events_block_base.js +37 -36
- package/core/events/events_block_change.js +130 -124
- package/core/events/events_block_create.js +73 -71
- package/core/events/events_block_delete.js +84 -82
- package/core/events/events_block_drag.js +50 -49
- package/core/events/events_block_move.js +147 -140
- package/core/events/events_bubble_open.js +51 -50
- package/core/events/events_click.js +48 -44
- package/core/events/events_comment_base.js +72 -69
- package/core/events/events_comment_change.js +63 -61
- package/core/events/events_comment_create.js +44 -42
- package/core/events/events_comment_delete.js +42 -40
- package/core/events/events_comment_move.js +106 -104
- package/core/events/events_marker_move.js +65 -64
- package/core/events/events_selected.js +46 -45
- package/core/events/events_theme_change.js +36 -35
- package/core/events/events_toolbox_item_select.js +46 -45
- package/core/events/events_trashcan_open.js +37 -36
- package/core/events/events_ui.js +47 -46
- package/core/events/events_ui_base.js +30 -29
- package/core/events/events_var_base.js +37 -36
- package/core/events/events_var_create.js +50 -48
- package/core/events/events_var_delete.js +50 -48
- package/core/events/events_var_rename.js +51 -49
- package/core/events/events_viewport.js +66 -65
- package/core/events/utils.js +29 -14
- package/core/events/workspace_events.js +49 -55
- package/core/extensions.js +4 -3
- package/core/field.js +1061 -997
- package/core/field_angle.js +462 -442
- package/core/field_checkbox.js +194 -182
- package/core/field_colour.js +519 -505
- package/core/field_dropdown.js +617 -598
- package/core/field_image.js +229 -220
- package/core/field_label.js +102 -91
- package/core/field_label_serializable.js +42 -41
- package/core/field_multilineinput.js +372 -358
- package/core/field_number.js +272 -253
- package/core/field_textinput.js +499 -467
- package/core/field_variable.js +458 -420
- package/core/flyout_base.js +1005 -952
- package/core/flyout_button.js +277 -260
- package/core/flyout_horizontal.js +304 -302
- package/core/flyout_metrics_manager.js +64 -64
- package/core/flyout_vertical.js +306 -300
- package/core/generator.js +459 -446
- package/core/gesture.js +829 -813
- package/core/grid.js +166 -163
- package/core/icon.js +168 -159
- package/core/inject.js +7 -5
- package/core/input.js +257 -248
- package/core/insertion_marker_manager.js +655 -624
- package/core/internal_constants.js +0 -129
- package/core/keyboard_nav/ast_node.js +605 -596
- package/core/keyboard_nav/basic_cursor.js +166 -165
- package/core/keyboard_nav/cursor.js +99 -97
- package/core/keyboard_nav/marker.js +83 -79
- package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
- package/core/marker_manager.js +153 -141
- package/core/menu.js +377 -372
- package/core/menuitem.js +223 -217
- package/core/metrics_manager.js +403 -390
- package/core/mutator.js +468 -437
- package/core/names.js +229 -188
- package/core/options.js +290 -284
- package/core/procedures.js +29 -17
- package/core/registry.js +19 -16
- package/core/rendered_connection.js +482 -463
- package/core/renderers/common/block_rendering.js +9 -3
- package/core/renderers/common/constants.js +1119 -1112
- package/core/renderers/common/debug.js +14 -0
- package/core/renderers/common/debugger.js +338 -316
- package/core/renderers/common/drawer.js +380 -370
- package/core/renderers/common/i_path_object.js +2 -2
- package/core/renderers/common/info.js +626 -618
- package/core/renderers/common/marker_svg.js +579 -541
- package/core/renderers/common/path_object.js +203 -200
- package/core/renderers/common/renderer.js +220 -218
- package/core/renderers/geras/constants.js +36 -36
- package/core/renderers/geras/drawer.js +155 -147
- package/core/renderers/geras/highlight_constants.js +244 -238
- package/core/renderers/geras/highlighter.js +231 -179
- package/core/renderers/geras/info.js +392 -369
- package/core/renderers/geras/measurables/inline_input.js +25 -19
- package/core/renderers/geras/measurables/statement_input.js +23 -17
- package/core/renderers/geras/path_object.js +106 -121
- package/core/renderers/geras/renderer.js +96 -98
- package/core/renderers/measurables/base.js +30 -18
- package/core/renderers/measurables/bottom_row.js +83 -80
- package/core/renderers/measurables/connection.js +22 -15
- package/core/renderers/measurables/external_value_input.js +35 -22
- package/core/renderers/measurables/field.js +35 -20
- package/core/renderers/measurables/hat.js +18 -13
- package/core/renderers/measurables/icon.js +24 -17
- package/core/renderers/measurables/in_row_spacer.js +15 -13
- package/core/renderers/measurables/inline_input.js +43 -33
- package/core/renderers/measurables/input_connection.js +41 -28
- package/core/renderers/measurables/input_row.js +50 -44
- package/core/renderers/measurables/jagged_edge.js +14 -12
- package/core/renderers/measurables/next_connection.js +16 -14
- package/core/renderers/measurables/output_connection.js +26 -20
- package/core/renderers/measurables/previous_connection.js +16 -15
- package/core/renderers/measurables/round_corner.js +20 -18
- package/core/renderers/measurables/row.js +184 -168
- package/core/renderers/measurables/spacer_row.js +38 -23
- package/core/renderers/measurables/square_corner.js +18 -16
- package/core/renderers/measurables/statement_input.js +23 -20
- package/core/renderers/measurables/top_row.js +88 -85
- package/core/renderers/minimalist/constants.js +8 -7
- package/core/renderers/minimalist/drawer.js +11 -10
- package/core/renderers/minimalist/info.js +18 -18
- package/core/renderers/minimalist/renderer.js +40 -39
- package/core/renderers/thrasos/info.js +258 -248
- package/core/renderers/thrasos/renderer.js +20 -20
- package/core/renderers/zelos/constants.js +898 -873
- package/core/renderers/zelos/drawer.js +186 -169
- package/core/renderers/zelos/info.js +502 -479
- package/core/renderers/zelos/marker_svg.js +129 -115
- package/core/renderers/zelos/measurables/bottom_row.js +31 -30
- package/core/renderers/zelos/measurables/inputs.js +22 -21
- package/core/renderers/zelos/measurables/row_elements.js +14 -13
- package/core/renderers/zelos/measurables/top_row.js +34 -33
- package/core/renderers/zelos/path_object.js +181 -180
- package/core/renderers/zelos/renderer.js +91 -92
- package/core/scrollbar.js +759 -713
- package/core/scrollbar_pair.js +250 -245
- package/core/serialization/blocks.js +26 -10
- package/core/serialization/workspaces.js +3 -2
- package/core/shortcut_registry.js +286 -277
- package/core/sprites.js +31 -0
- package/core/theme.js +135 -141
- package/core/theme_manager.js +147 -143
- package/core/toolbox/category.js +602 -576
- package/core/toolbox/collapsible_category.js +226 -227
- package/core/toolbox/separator.js +70 -61
- package/core/toolbox/toolbox.js +934 -927
- package/core/toolbox/toolbox_item.js +115 -99
- package/core/tooltip.js +108 -35
- package/core/touch.js +8 -3
- package/core/touch_gesture.js +254 -251
- package/core/trashcan.js +606 -595
- package/core/utils/coordinate.js +97 -95
- package/core/utils/dom.js +2 -2
- package/core/utils/global.js +2 -0
- package/core/utils/rect.js +41 -37
- package/core/utils/sentinel.js +25 -0
- package/core/utils/size.js +30 -27
- package/core/utils/svg.js +18 -16
- package/core/variable_map.js +325 -341
- package/core/variable_model.js +55 -54
- package/core/variables.js +9 -2
- package/core/variables_dynamic.js +3 -1
- package/core/warning.js +126 -120
- package/core/widgetdiv.js +4 -4
- package/core/workspace.js +685 -664
- package/core/workspace_audio.js +124 -118
- package/core/workspace_comment.js +308 -298
- package/core/workspace_comment_svg.js +1029 -951
- package/core/workspace_drag_surface_svg.js +147 -140
- package/core/workspace_dragger.js +70 -71
- package/core/workspace_svg.js +2322 -2297
- package/core/xml.js +30 -20
- package/core/zoom_controls.js +431 -439
- package/generators/dart/colour.js +56 -64
- package/generators/dart/lists.js +61 -50
- package/generators/dart/math.js +160 -148
- package/generators/dart/text.js +83 -61
- package/generators/javascript/colour.js +37 -34
- package/generators/javascript/lists.js +50 -43
- package/generators/javascript/math.js +123 -139
- package/generators/javascript/text.js +67 -81
- package/generators/lua/colour.js +25 -23
- package/generators/lua/lists.js +97 -69
- package/generators/lua/logic.js +1 -2
- package/generators/lua/math.js +182 -144
- package/generators/lua/text.js +116 -99
- package/generators/php/colour.js +38 -32
- package/generators/php/lists.js +109 -89
- package/generators/php/math.js +90 -81
- package/generators/php/text.js +63 -61
- package/generators/python/colour.js +18 -18
- package/generators/python/lists.js +38 -30
- package/generators/python/loops.js +12 -8
- package/generators/python/math.js +104 -106
- package/generators/python/text.js +34 -30
- package/msg/smn.js +436 -0
- package/package.json +7 -6
- package/blocks/all.js +0 -23
package/core/field_colour.js
CHANGED
|
@@ -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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
*
|
|
56
|
-
* @
|
|
57
|
-
* @
|
|
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
|
-
|
|
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
|
-
*
|
|
63
|
-
* @
|
|
64
|
-
* @private
|
|
178
|
+
* Create the block UI for this colour field.
|
|
179
|
+
* @package
|
|
65
180
|
*/
|
|
66
|
-
|
|
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
|
-
*
|
|
70
|
-
* @type {?browserEvents.Data}
|
|
71
|
-
* @private
|
|
194
|
+
* @override
|
|
72
195
|
*/
|
|
73
|
-
|
|
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
|
-
*
|
|
77
|
-
* @
|
|
78
|
-
* @
|
|
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
|
-
|
|
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
|
-
*
|
|
84
|
-
* @
|
|
85
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
91
|
-
* @
|
|
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
|
-
|
|
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
|
-
*
|
|
98
|
-
* @
|
|
99
|
-
*
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
*
|
|
165
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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);
|