field-segment.component.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /**
  2. * AccessibleBlockly
  3. *
  4. * Copyright 2016 Google Inc.
  5. * https://developers.google.com/blockly/
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the 'License');
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an 'AS IS' BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. /**
  20. * @fileoverview Angular2 Component that renders a "field segment" (a group
  21. * of non-editable Blockly.Field followed by 0 or 1 editable Blockly.Field)
  22. * in a block. Also handles any interactions with the field.
  23. * @author madeeha@google.com (Madeeha Ghori)
  24. */
  25. blocklyApp.FieldSegmentComponent = ng.core
  26. .Component({
  27. selector: 'blockly-field-segment',
  28. template: `
  29. <template [ngIf]="!mainField">
  30. <label [id]="mainFieldId">{{getPrefixText()}}</label>
  31. </template>
  32. <template [ngIf]="mainField">
  33. <template [ngIf]="isTextInput()">
  34. {{getPrefixText()}}
  35. <input [id]="mainFieldId" type="text" [disabled]="disabled"
  36. [ngModel]="mainField.getValue()" (ngModelChange)="mainField.setValue($event)"
  37. [attr.aria-label]="getFieldDescription() + (disabled ? 'Disabled text field' : 'Press Enter to edit text')"
  38. tabindex="-1">
  39. </template>
  40. <template [ngIf]="isNumberInput()">
  41. {{getPrefixText()}}
  42. <input [id]="mainFieldId" type="number" [disabled]="disabled"
  43. [ngModel]="mainField.getValue()" (ngModelChange)="setNumberValue($event)"
  44. [attr.aria-label]="getFieldDescription() + (disabled ? 'Disabled number field' : 'Press Enter to edit number')"
  45. tabindex="-1">
  46. </template>
  47. <template [ngIf]="isDropdown()">
  48. <label [id]="mainFieldId" [attr.aria-label]="getFieldDescription() + ' Move right to view submenu'">
  49. {{getFieldDescription()}}
  50. </label>
  51. <ol role="group">
  52. <li [id]="idMap[optionValue]" role="treeitem" *ngFor="#optionValue of getOptions()"
  53. [attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')"
  54. [attr.aria-level]="level" [attr.aria-selected]="mainField.getValue() == optionValue"
  55. class="blocklyDropdownListItem">
  56. <button [id]="idMap[optionValue + 'Button']" (click)="handleDropdownChange(mainField, optionValue)"
  57. [disabled]="disabled" tabindex="-1"
  58. [attr.aria-label]="optionText[optionValue] + ' Press Enter to select this value'">
  59. {{optionText[optionValue]}}
  60. </button>
  61. </li>
  62. </ol>
  63. </template>
  64. </template>
  65. `,
  66. inputs: ['prefixFields', 'mainField', 'mainFieldId', 'level'],
  67. pipes: [blocklyApp.TranslatePipe]
  68. })
  69. .Class({
  70. constructor: [
  71. blocklyApp.NotificationsService, blocklyApp.UtilsService,
  72. function(_notificationsService, _utilsService) {
  73. this.optionText = {
  74. keys: []
  75. };
  76. this.notificationsService = _notificationsService;
  77. this.utilsService = _utilsService;
  78. }],
  79. ngOnInit: function() {
  80. var elementsNeedingIds = this.generateElementNames(this.mainField);
  81. // Warning: this assumes that the elements returned by
  82. // this.generateElementNames() are unique.
  83. this.idMap = this.utilsService.generateIds(elementsNeedingIds);
  84. },
  85. getPrefixText: function() {
  86. var prefixTexts = this.prefixFields.map(function(prefixField) {
  87. return prefixField.getText();
  88. });
  89. return prefixTexts.join(' ');
  90. },
  91. getFieldDescription: function() {
  92. var description = this.mainField.getText();
  93. if (this.prefixFields.length > 0) {
  94. description = this.getPrefixText() + ': ' + description;
  95. }
  96. return description;
  97. },
  98. setNumberValue: function(newValue) {
  99. // Do not permit a residual value of NaN after a backspace event.
  100. this.mainField.setValue(newValue || 0);
  101. },
  102. generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
  103. return mainLabel + ' ' + secondLabel;
  104. },
  105. generateElementNames: function() {
  106. var elementNames = [];
  107. if (this.isDropdown()) {
  108. var keys = this.getOptions();
  109. for (var i = 0; i < keys.length; i++){
  110. elementNames.push(keys[i], keys[i] + 'Button');
  111. }
  112. }
  113. return elementNames;
  114. },
  115. isNumberInput: function() {
  116. return this.mainField instanceof Blockly.FieldNumber;
  117. },
  118. isTextInput: function() {
  119. return this.mainField instanceof Blockly.FieldTextInput &&
  120. !(this.mainField instanceof Blockly.FieldNumber);
  121. },
  122. isDropdown: function() {
  123. return this.mainField instanceof Blockly.FieldDropdown;
  124. },
  125. isCheckbox: function() {
  126. return this.mainField instanceof Blockly.FieldCheckbox;
  127. },
  128. isTextField: function() {
  129. return !(this.mainField instanceof Blockly.FieldTextInput) &&
  130. !(this.mainField instanceof Blockly.FieldDropdown) &&
  131. !(this.mainField instanceof Blockly.FieldCheckbox);
  132. },
  133. hasVisibleText: function() {
  134. var text = this.mainField.getText().trim();
  135. return !!text;
  136. },
  137. getOptions: function() {
  138. if (this.optionText.keys.length) {
  139. return this.optionText.keys;
  140. }
  141. var options = this.mainField.getOptions_();
  142. for (var i = 0; i < options.length; i++) {
  143. var tuple = options[i];
  144. this.optionText[tuple[1]] = tuple[0];
  145. this.optionText.keys.push(tuple[1]);
  146. }
  147. return this.optionText.keys;
  148. },
  149. handleDropdownChange: function(field, optionValue) {
  150. if (optionValue == 'NO_ACTION') {
  151. return;
  152. }
  153. if (this.mainField instanceof Blockly.FieldVariable) {
  154. Blockly.FieldVariable.dropdownChange.call(this.mainField, optionValue);
  155. } else {
  156. this.mainField.setValue(optionValue);
  157. }
  158. this.notificationsService.setStatusMessage(
  159. 'Selected option ' + this.optionText[optionValue]);
  160. }
  161. });