EditorActions.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import {
  2. forEach,
  3. isArray
  4. } from 'min-dash';
  5. var NOT_REGISTERED_ERROR = 'is not a registered action',
  6. IS_REGISTERED_ERROR = 'is already registered';
  7. /**
  8. * An interface that provides access to modeling actions by decoupling
  9. * the one who requests the action to be triggered and the trigger itself.
  10. *
  11. * It's possible to add new actions by registering them with ´registerAction´
  12. * and likewise unregister existing ones with ´unregisterAction´.
  13. *
  14. *
  15. * ## Life-Cycle and configuration
  16. *
  17. * The editor actions will wait for diagram initialization before
  18. * registering default actions _and_ firing an `editorActions.init` event.
  19. *
  20. * Interested parties may listen to the `editorActions.init` event with
  21. * low priority to check, which actions got registered. Other components
  22. * may use the event to register their own actions via `registerAction`.
  23. *
  24. * @param {EventBus} eventBus
  25. * @param {Injector} injector
  26. */
  27. export default function EditorActions(eventBus, injector) {
  28. // initialize actions
  29. this._actions = {};
  30. var self = this;
  31. eventBus.on('diagram.init', function() {
  32. // all diagram modules got loaded; check which ones
  33. // are available and register the respective default actions
  34. self._registerDefaultActions(injector);
  35. // ask interested parties to register available editor
  36. // actions on diagram initialization
  37. eventBus.fire('editorActions.init', {
  38. editorActions: self
  39. });
  40. });
  41. }
  42. EditorActions.$inject = [
  43. 'eventBus',
  44. 'injector'
  45. ];
  46. /**
  47. * Register default actions.
  48. *
  49. * @param {Injector} injector
  50. */
  51. EditorActions.prototype._registerDefaultActions = function(injector) {
  52. // (1) retrieve optional components to integrate with
  53. var commandStack = injector.get('commandStack', false);
  54. var modeling = injector.get('modeling', false);
  55. var selection = injector.get('selection', false);
  56. var zoomScroll = injector.get('zoomScroll', false);
  57. var copyPaste = injector.get('copyPaste', false);
  58. var canvas = injector.get('canvas', false);
  59. var rules = injector.get('rules', false);
  60. var mouseTracking = injector.get('mouseTracking', false);
  61. var keyboardMove = injector.get('keyboardMove', false);
  62. var keyboardMoveSelection = injector.get('keyboardMoveSelection', false);
  63. // (2) check components and register actions
  64. if (commandStack) {
  65. this.register('undo', function() {
  66. commandStack.undo();
  67. });
  68. this.register('redo', function() {
  69. commandStack.redo();
  70. });
  71. }
  72. if (copyPaste && selection) {
  73. this.register('copy', function() {
  74. var selectedElements = selection.get();
  75. copyPaste.copy(selectedElements);
  76. });
  77. }
  78. if (mouseTracking && copyPaste) {
  79. this.register('paste', function() {
  80. var context = mouseTracking.getHoverContext();
  81. copyPaste.paste(context);
  82. });
  83. }
  84. if (zoomScroll) {
  85. this.register('stepZoom', function(opts) {
  86. zoomScroll.stepZoom(opts.value);
  87. });
  88. }
  89. if (canvas) {
  90. this.register('zoom', function(opts) {
  91. canvas.zoom(opts.value);
  92. });
  93. }
  94. if (modeling && selection && rules) {
  95. this.register('removeSelection', function() {
  96. var selectedElements = selection.get();
  97. if (!selectedElements.length) {
  98. return;
  99. }
  100. var allowed = rules.allowed('elements.delete', { elements: selectedElements }),
  101. removableElements;
  102. if (allowed === false) {
  103. return;
  104. }
  105. else if (isArray(allowed)) {
  106. removableElements = allowed;
  107. }
  108. else {
  109. removableElements = selectedElements;
  110. }
  111. if (removableElements.length) {
  112. modeling.removeElements(removableElements.slice());
  113. }
  114. });
  115. }
  116. if (keyboardMove) {
  117. this.register('moveCanvas', function(opts) {
  118. keyboardMove.moveCanvas(opts);
  119. });
  120. }
  121. if (keyboardMoveSelection) {
  122. this.register('moveSelection', function(opts) {
  123. keyboardMoveSelection.moveSelection(opts.direction, opts.accelerated);
  124. });
  125. }
  126. };
  127. /**
  128. * Triggers a registered action
  129. *
  130. * @param {String} action
  131. * @param {Object} opts
  132. *
  133. * @return {Unknown} Returns what the registered listener returns
  134. */
  135. EditorActions.prototype.trigger = function(action, opts) {
  136. if (!this._actions[action]) {
  137. throw error(action, NOT_REGISTERED_ERROR);
  138. }
  139. return this._actions[action](opts);
  140. };
  141. /**
  142. * Registers a collections of actions.
  143. * The key of the object will be the name of the action.
  144. *
  145. * @example
  146. * ´´´
  147. * var actions = {
  148. * spaceTool: function() {
  149. * spaceTool.activateSelection();
  150. * },
  151. * lassoTool: function() {
  152. * lassoTool.activateSelection();
  153. * }
  154. * ];
  155. *
  156. * editorActions.register(actions);
  157. *
  158. * editorActions.isRegistered('spaceTool'); // true
  159. * ´´´
  160. *
  161. * @param {Object} actions
  162. */
  163. EditorActions.prototype.register = function(actions, listener) {
  164. var self = this;
  165. if (typeof actions === 'string') {
  166. return this._registerAction(actions, listener);
  167. }
  168. forEach(actions, function(listener, action) {
  169. self._registerAction(action, listener);
  170. });
  171. };
  172. /**
  173. * Registers a listener to an action key
  174. *
  175. * @param {String} action
  176. * @param {Function} listener
  177. */
  178. EditorActions.prototype._registerAction = function(action, listener) {
  179. if (this.isRegistered(action)) {
  180. throw error(action, IS_REGISTERED_ERROR);
  181. }
  182. this._actions[action] = listener;
  183. };
  184. /**
  185. * Unregister an existing action
  186. *
  187. * @param {String} action
  188. */
  189. EditorActions.prototype.unregister = function(action) {
  190. if (!this.isRegistered(action)) {
  191. throw error(action, NOT_REGISTERED_ERROR);
  192. }
  193. this._actions[action] = undefined;
  194. };
  195. /**
  196. * Returns the number of actions that are currently registered
  197. *
  198. * @return {Number}
  199. */
  200. EditorActions.prototype.getActions = function() {
  201. return Object.keys(this._actions);
  202. };
  203. /**
  204. * Checks wether the given action is registered
  205. *
  206. * @param {String} action
  207. *
  208. * @return {Boolean}
  209. */
  210. EditorActions.prototype.isRegistered = function(action) {
  211. return !!this._actions[action];
  212. };
  213. function error(action, message) {
  214. return new Error(action + ' ' + message);
  215. }