expand.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. define(function(require, exports, module) {
  2. var kity = require('../core/kity');
  3. var utils = require('../core/utils');
  4. var keymap = require('../core/keymap');
  5. var MinderNode = require('../core/node');
  6. var Command = require('../core/command');
  7. var Module = require('../core/module');
  8. var Renderer = require('../core/render');
  9. Module.register('Expand', function() {
  10. var minder = this;
  11. var EXPAND_STATE_DATA = 'expandState',
  12. STATE_EXPAND = 'expand',
  13. STATE_COLLAPSE = 'collapse';
  14. // 将展开的操作和状态读取接口拓展到 MinderNode 上
  15. kity.extendClass(MinderNode, {
  16. /**
  17. * 展开节点
  18. * @param {Policy} policy 展开的策略,默认为 KEEP_STATE
  19. */
  20. expand: function() {
  21. this.setData(EXPAND_STATE_DATA, STATE_EXPAND);
  22. return this;
  23. },
  24. /**
  25. * 收起节点
  26. */
  27. collapse: function() {
  28. this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE);
  29. return this;
  30. },
  31. /**
  32. * 判断节点当前的状态是否为展开
  33. */
  34. isExpanded: function() {
  35. var expanded = this.getData(EXPAND_STATE_DATA) !== STATE_COLLAPSE;
  36. return expanded && (this.isRoot() || this.parent.isExpanded());
  37. },
  38. /**
  39. * 判断节点当前的状态是否为收起
  40. */
  41. isCollapsed: function() {
  42. return !this.isExpanded();
  43. }
  44. });
  45. /**
  46. * @command Expand
  47. * @description 展开当前选中的节点,保证其可见
  48. * @param {bool} justParents 是否只展开到父亲
  49. * * `false` - (默认)保证选中的节点以及其子树可见
  50. * * `true` - 只保证选中的节点可见,不展开其子树
  51. * @state
  52. * 0: 当前有选中的节点
  53. * -1: 当前没有选中的节点
  54. */
  55. var ExpandCommand = kity.createClass('ExpandCommand', {
  56. base: Command,
  57. execute: function(km, justParents) {
  58. var node = km.getSelectedNode();
  59. if (!node) return;
  60. if (justParents) {
  61. node = node.parent;
  62. }
  63. while (node.parent) {
  64. node.expand();
  65. node = node.parent;
  66. }
  67. node.renderTree();
  68. km.layout(100);
  69. },
  70. queryState: function(km) {
  71. var node = km.getSelectedNode();
  72. return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
  73. }
  74. });
  75. /**
  76. * @command ExpandToLevel
  77. * @description 展开脑图到指定的层级
  78. * @param {number} level 指定展开到的层级,最少值为 1。
  79. * @state
  80. * 0: 一直可用
  81. */
  82. var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', {
  83. base: Command,
  84. execute: function(km, level) {
  85. km.getRoot().traverse(function(node) {
  86. if (node.getLevel() < level) node.expand();
  87. if (node.getLevel() == level && !node.isLeaf()) node.collapse();
  88. });
  89. km.refresh(100);
  90. },
  91. enableReadOnly: true
  92. });
  93. /**
  94. * @command Collapse
  95. * @description 收起当前节点的子树
  96. * @state
  97. * 0: 当前有选中的节点
  98. * -1: 当前没有选中的节点
  99. */
  100. var CollapseCommand = kity.createClass('CollapseCommand', {
  101. base: Command,
  102. execute: function(km) {
  103. var node = km.getSelectedNode();
  104. if (!node) return;
  105. node.collapse();
  106. node.renderTree();
  107. km.layout();
  108. },
  109. queryState: function(km) {
  110. var node = km.getSelectedNode();
  111. return node && !node.isRoot() && node.isExpanded() ? 0 : -1;
  112. }
  113. });
  114. var Expander = kity.createClass('Expander', {
  115. base: kity.Group,
  116. constructor: function(node) {
  117. this.callBase();
  118. this.radius = 6;
  119. this.outline = new kity.Circle(this.radius).stroke('gray').fill('white');
  120. this.sign = new kity.Path().stroke('gray');
  121. this.addShapes([this.outline, this.sign]);
  122. this.initEvent(node);
  123. this.setId(utils.uuid('node_expander'));
  124. this.setStyle('cursor', 'pointer');
  125. },
  126. initEvent: function(node) {
  127. this.on('mousedown', function(e) {
  128. minder.select([node], true);
  129. if (node.isExpanded()) {
  130. node.collapse();
  131. } else {
  132. node.expand();
  133. }
  134. node.renderTree().getMinder().layout(100);
  135. node.getMinder().fire('contentchange');
  136. e.stopPropagation();
  137. e.preventDefault();
  138. });
  139. this.on('dblclick click mouseup', function(e) {
  140. e.stopPropagation();
  141. e.preventDefault();
  142. });
  143. },
  144. setState: function(state) {
  145. if (state == 'hide') {
  146. this.setVisible(false);
  147. return;
  148. }
  149. this.setVisible(true);
  150. var pathData = ['M', 1.5 - this.radius, 0, 'L', this.radius - 1.5, 0];
  151. if (state == STATE_COLLAPSE) {
  152. pathData.push(['M', 0, 1.5 - this.radius, 'L', 0, this.radius - 1.5]);
  153. }
  154. this.sign.setPathData(pathData);
  155. }
  156. });
  157. var ExpanderRenderer = kity.createClass('ExpanderRenderer', {
  158. base: Renderer,
  159. create: function(node) {
  160. if (node.isRoot()) return;
  161. this.expander = new Expander(node);
  162. node.getRenderContainer().prependShape(this.expander);
  163. node.expanderRenderer = this;
  164. this.node = node;
  165. return this.expander;
  166. },
  167. shouldRender: function(node) {
  168. return !node.isRoot();
  169. },
  170. update: function(expander, node, box) {
  171. if (!node.parent) return;
  172. var visible = node.parent.isExpanded();
  173. expander.setState(visible && node.children.length ? node.getData(EXPAND_STATE_DATA) : 'hide');
  174. var vector = node.getLayoutVectorIn().normalize(expander.radius + node.getStyle('stroke-width'));
  175. var position = node.getVertexIn().offset(vector.reverse());
  176. this.expander.setTranslate(position);
  177. }
  178. });
  179. return {
  180. commands: {
  181. 'expand': ExpandCommand,
  182. 'expandtolevel': ExpandToLevelCommand,
  183. 'collapse': CollapseCommand
  184. },
  185. events: {
  186. 'layoutapply': function(e) {
  187. var r = e.node.getRenderer('ExpanderRenderer');
  188. if (r.getRenderShape()) {
  189. r.update(r.getRenderShape(), e.node);
  190. }
  191. },
  192. 'beforerender': function(e) {
  193. var node = e.node;
  194. var visible = !node.parent || node.parent.isExpanded();
  195. var minder = this;
  196. node.getRenderContainer().setVisible(visible);
  197. if (!visible) e.stopPropagation();
  198. },
  199. 'normal.keydown': function(e) {
  200. if (this.getStatus() == 'textedit') return;
  201. if (e.originEvent.keyCode == keymap['/']) {
  202. var node = this.getSelectedNode();
  203. if (!node || node == this.getRoot()) return;
  204. var expanded = node.isExpanded();
  205. this.getSelectedNodes().forEach(function(node) {
  206. if (expanded) node.collapse();
  207. else node.expand();
  208. node.renderTree();
  209. });
  210. this.layout(100);
  211. this.fire('contentchange');
  212. e.preventDefault();
  213. e.stopPropagationImmediately();
  214. }
  215. if (e.isShortcutKey('Alt+`')) {
  216. this.execCommand('expandtolevel', 9999);
  217. }
  218. for (var i = 1; i < 6; i++) {
  219. if (e.isShortcutKey('Alt+' + i)) {
  220. this.execCommand('expandtolevel', i);
  221. }
  222. }
  223. }
  224. },
  225. renderers: {
  226. outside: ExpanderRenderer
  227. },
  228. contextmenu: [{
  229. command: 'expandtoleaf',
  230. query: function() {
  231. return !minder.getSelectedNode();
  232. },
  233. fn: function(minder) {
  234. minder.execCommand('expandtolevel', 9999);
  235. }
  236. }, {
  237. command: 'expandtolevel1',
  238. query: function() {
  239. return !minder.getSelectedNode();
  240. },
  241. fn: function(minder) {
  242. minder.execCommand('expandtolevel', 1);
  243. }
  244. }, {
  245. command: 'expandtolevel2',
  246. query: function() {
  247. return !minder.getSelectedNode();
  248. },
  249. fn: function(minder) {
  250. minder.execCommand('expandtolevel', 2);
  251. }
  252. },{
  253. command: 'expandtolevel3',
  254. query: function() {
  255. return !minder.getSelectedNode();
  256. },
  257. fn: function(minder) {
  258. minder.execCommand('expandtolevel', 3);
  259. }
  260. }, {
  261. divider: true
  262. }]
  263. };
  264. });
  265. });