toolbox-tree.component.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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 details how blocks are
  21. * rendered in the toolbox in AccessibleBlockly. Also handles any interactions
  22. * with the blocks.
  23. * @author madeeha@google.com (Madeeha Ghori)
  24. */
  25. blocklyApp.ToolboxTreeComponent = ng.core
  26. .Component({
  27. selector: 'blockly-toolbox-tree',
  28. template: `
  29. <li [id]="idMap['toolboxBlockRoot']" role="treeitem"
  30. [ngClass]="{blocklyHasChildren: displayBlockMenu}"
  31. [attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['toolboxBlockSummary'], 'blockly-toolbox-block')"
  32. [attr.aria-level]="level">
  33. <label #toolboxBlockSummary [id]="idMap['toolboxBlockSummary']">{{getBlockDescription()}}</label>
  34. <ol role="group" *ngIf="displayBlockMenu">
  35. <li [id]="idMap['blockCopy']" role="treeitem" *ngIf="!isWorkspaceEmpty()"
  36. [attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['blockCopyButton'], 'blockly-button')"
  37. [attr.aria-level]="level + 1">
  38. <button [id]="idMap['blockCopyButton']" (click)="copyToClipboard()" tabindex="-1">
  39. {{'COPY_TO_CLIPBOARD'|translate}}
  40. </button>
  41. </li>
  42. <li [id]="idMap['sendToSelected']" role="treeitem" *ngIf="!isWorkspaceEmpty()"
  43. [attr.aria-label]="getAriaLabelForCopyToMarkedSpotButton()"
  44. [attr.aria-level]="level + 1"
  45. [attr.aria-disabled]="!canBeCopiedToMarkedConnection()">
  46. <button [id]="idMap['sendToSelectedButton']" (click)="copyToMarkedSpot()"
  47. [disabled]="!canBeCopiedToMarkedConnection()" tabindex="-1">
  48. {{'COPY_TO_MARKED_SPOT'|translate}}
  49. </button>
  50. </li>
  51. <li [id]="idMap['workspaceCopy']" role="treeitem"
  52. [attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['workspaceCopyButton'], 'blockly-button')"
  53. [attr.aria-level]="level + 1">
  54. <button [id]="idMap['workspaceCopyButton']" (click)="copyToWorkspace()" tabindex="-1">
  55. {{'COPY_TO_WORKSPACE'|translate}}
  56. </button>
  57. </li>
  58. </ol>
  59. </li>
  60. `,
  61. directives: [ng.core.forwardRef(function() {
  62. return blocklyApp.ToolboxTreeComponent;
  63. })],
  64. inputs: [
  65. 'block', 'displayBlockMenu', 'level', 'tree', 'isFirstToolboxTree'],
  66. pipes: [blocklyApp.TranslatePipe]
  67. })
  68. .Class({
  69. constructor: [
  70. blocklyApp.ClipboardService, blocklyApp.NotificationsService,
  71. blocklyApp.TreeService, blocklyApp.UtilsService,
  72. function(
  73. _clipboardService, _notificationsService,
  74. _treeService, _utilsService) {
  75. this.clipboardService = _clipboardService;
  76. this.notificationsService = _notificationsService;
  77. this.treeService = _treeService;
  78. this.utilsService = _utilsService;
  79. }],
  80. ngOnInit: function() {
  81. var idKeys = ['toolboxBlockRoot', 'toolboxBlockSummary'];
  82. if (this.displayBlockMenu) {
  83. idKeys = idKeys.concat([
  84. 'workspaceCopy', 'workspaceCopyButton', 'sendToSelected',
  85. 'sendToSelectedButton', 'blockCopy', 'blockCopyButton']);
  86. }
  87. this.idMap = {};
  88. for (var i = 0; i < idKeys.length; i++) {
  89. this.idMap[idKeys[i]] = this.block.id + idKeys[i];
  90. }
  91. },
  92. ngAfterViewInit: function() {
  93. // If this is the first tree in the category-less toolbox, set its active
  94. // descendant after the ids have been computed.
  95. // Note that a timeout is needed here in order to trigger Angular
  96. // change detection.
  97. if (this.isFirstToolboxTree) {
  98. var that = this;
  99. setTimeout(function() {
  100. that.treeService.setActiveDesc(
  101. that.idMap['toolboxBlockRoot'], 'blockly-toolbox-tree');
  102. });
  103. }
  104. },
  105. getAriaLabelForCopyToMarkedSpotButton: function() {
  106. // TODO(sll): Find a way to make this more like the other buttons.
  107. var ariaLabel = 'Attach to link button';
  108. if (!this.clipboardService.isAnyConnectionMarked()) {
  109. ariaLabel += ', unavailable. Add a link in the workspace first.';
  110. }
  111. return ariaLabel;
  112. },
  113. isWorkspaceEmpty: function() {
  114. return this.utilsService.isWorkspaceEmpty();
  115. },
  116. getBlockDescription: function() {
  117. return this.utilsService.getBlockDescription(this.block);
  118. },
  119. generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
  120. return this.utilsService.generateAriaLabelledByAttr(
  121. mainLabel, secondLabel);
  122. },
  123. canBeCopiedToMarkedConnection: function() {
  124. return this.clipboardService.canBeCopiedToMarkedConnection(this.block);
  125. },
  126. copyToClipboard: function() {
  127. this.clipboardService.copy(this.block);
  128. this.notificationsService.setStatusMessage(
  129. this.getBlockDescription() + ' ' + Blockly.Msg.COPIED_BLOCK_MSG);
  130. },
  131. copyToWorkspace: function() {
  132. var blockDescription = this.getBlockDescription();
  133. var xml = Blockly.Xml.blockToDom(this.block);
  134. var newBlockId = Blockly.Xml.domToBlock(blocklyApp.workspace, xml).id;
  135. var that = this;
  136. setTimeout(function() {
  137. that.treeService.focusOnBlock(newBlockId);
  138. that.notificationsService.setStatusMessage(
  139. blockDescription + ' added to workspace. ' +
  140. 'Now on added block in workspace.');
  141. });
  142. },
  143. copyToMarkedSpot: function() {
  144. var blockDescription = this.getBlockDescription();
  145. // Clean up the active desc for the destination tree.
  146. var oldDestinationTreeId = this.treeService.getTreeIdForBlock(
  147. this.clipboardService.getMarkedConnectionBlock().id);
  148. this.treeService.clearActiveDesc(oldDestinationTreeId);
  149. var newBlockId = this.clipboardService.pasteToMarkedConnection(
  150. this.block);
  151. // Invoke a digest cycle, so that the DOM settles.
  152. var that = this;
  153. setTimeout(function() {
  154. that.treeService.focusOnBlock(newBlockId);
  155. var newDestinationTreeId = that.treeService.getTreeIdForBlock(
  156. newBlockId);
  157. if (newDestinationTreeId != oldDestinationTreeId) {
  158. // It is possible for the tree ID for the pasted block to change
  159. // after the paste operation, e.g. when inserting a block between two
  160. // existing blocks that are joined together. In this case, we need to
  161. // also reset the active desc for the old destination tree.
  162. that.treeService.initActiveDesc(oldDestinationTreeId);
  163. }
  164. that.notificationsService.setStatusMessage(
  165. blockDescription + ' connected. ' +
  166. 'Now on copied block in workspace.');
  167. });
  168. }
  169. });