@7polo/kityminder-core
Version:
KityMinder Core Implement
295 lines (266 loc) • 10.6 kB
JavaScript
define(function(require, exports, module) {
var kity = require('../core/kity');
var utils = require('../core/utils');
var keymap = require('../core/keymap');
var MinderNode = require('../core/node');
var Command = require('../core/command');
var Module = require('../core/module');
var Renderer = require('../core/render');
Module.register('Expand', function() {
var minder = this;
var EXPAND_STATE_DATA = 'expandState',
STATE_EXPAND = 'expand',
STATE_COLLAPSE = 'collapse';
// 将展开的操作和状态读取接口拓展到 MinderNode 上
kity.extendClass(MinderNode, {
/**
* 展开节点
* @param {Policy} policy 展开的策略,默认为 KEEP_STATE
*/
expand: function() {
this.setData(EXPAND_STATE_DATA, STATE_EXPAND);
return this;
},
/**
* 收起节点
*/
collapse: function() {
this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE);
return this;
},
/**
* 判断节点当前的状态是否为展开
*/
isExpanded: function() {
var expanded = this.getData(EXPAND_STATE_DATA) !== STATE_COLLAPSE;
return expanded && (this.isRoot() || this.parent.isExpanded());
},
/**
* 判断节点当前的状态是否为收起
*/
isCollapsed: function() {
return !this.isExpanded();
}
});
/**
* @command Expand
* @description 展开当前选中的节点,保证其可见
* @param {bool} justParents 是否只展开到父亲
* * `false` - (默认)保证选中的节点以及其子树可见
* * `true` - 只保证选中的节点可见,不展开其子树
* @state
* 0: 当前有选中的节点
* -1: 当前没有选中的节点
*/
var ExpandCommand = kity.createClass('ExpandCommand', {
base: Command,
execute: function(km, justParents) {
var node = km.getSelectedNode();
if (!node) return;
if (justParents) {
node = node.parent;
}
while (node.parent) {
node.expand();
node = node.parent;
}
node.renderTree();
km.layout(100);
},
queryState: function(km) {
var node = km.getSelectedNode();
return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
}
});
/**
* @command ExpandToLevel
* @description 展开脑图到指定的层级
* @param {number} level 指定展开到的层级,最少值为 1。
* @state
* 0: 一直可用
*/
var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', {
base: Command,
execute: function(km, level) {
km.getRoot().traverse(function(node) {
if (node.getLevel() < level) node.expand();
if (node.getLevel() == level && !node.isLeaf()) node.collapse();
});
km.refresh(100);
},
enableReadOnly: true
});
/**
* @command Collapse
* @description 收起当前节点的子树
* @state
* 0: 当前有选中的节点
* -1: 当前没有选中的节点
*/
var CollapseCommand = kity.createClass('CollapseCommand', {
base: Command,
execute: function(km) {
var node = km.getSelectedNode();
if (!node) return;
node.collapse();
node.renderTree();
km.layout();
},
queryState: function(km) {
var node = km.getSelectedNode();
return node && !node.isRoot() && node.isExpanded() ? 0 : -1;
}
});
var Expander = kity.createClass('Expander', {
base: kity.Group,
constructor: function(node) {
this.callBase2(kity.Group, 'constructor', []);
this.radius = 6;
this.outline = new kity.Circle(this.radius).stroke('gray').fill('white');
this.sign = new kity.Path().stroke('gray');
this.addShapes([this.outline, this.sign]);
this.initEvent(node);
this.setId(utils.uuid('node_expander'));
this.setStyle('cursor', 'pointer');
},
initEvent: function(node) {
var target = this.node;
target.addEventListener('mousedown', function(e) {
minder.select([node], true);
if (node.isExpanded()) {
node.collapse();
} else {
node.expand();
}
node.renderTree().getMinder().layout(100);
node.getMinder().fire('contentchange');
e.stopPropagation();
e.preventDefault();
});
target.addEventListener('dblclick click mouseup', function(e) {
e.stopPropagation();
e.preventDefault();
});
},
setState: function(state) {
if (state == 'hide') {
this.setVisible(false);
return;
}
this.setVisible(true);
var pathData = ['M', 1.5 - this.radius, 0, 'L', this.radius - 1.5, 0];
if (state == STATE_COLLAPSE) {
pathData.push(['M', 0, 1.5 - this.radius, 'L', 0, this.radius - 1.5]);
}
this.sign.setPathData(pathData);
}
});
var ExpanderRenderer = kity.createClass('ExpanderRenderer', {
base: Renderer,
create: function(node) {
if (node.isRoot()) return;
this.expander = new Expander(node);
node.getRenderContainer().prependShape(this.expander);
node.expanderRenderer = this;
this.node = node;
return this.expander;
},
shouldRender: function(node) {
return !node.isRoot();
},
update: function(expander, node, box) {
if (!node.parent) return;
var visible = node.parent.isExpanded();
expander.setState(visible && node.children.length ? node.getData(EXPAND_STATE_DATA) : 'hide');
var vector = node.getLayoutVectorIn().normalize(expander.radius + node.getStyle('stroke-width'));
var position = node.getVertexIn().offset(vector.reverse());
this.expander.setTranslate(position);
}
});
return {
commands: {
'expand': ExpandCommand,
'expandtolevel': ExpandToLevelCommand,
'collapse': CollapseCommand
},
events: {
'layoutapply': function(e) {
var r = e.node.getRenderer('ExpanderRenderer');
if (r.getRenderShape()) {
r.update(r.getRenderShape(), e.node);
}
},
'beforerender': function(e) {
var node = e.node;
var visible = !node.parent || node.parent.isExpanded();
var minder = this;
node.getRenderContainer().setVisible(visible);
if (!visible) e.stopPropagation();
},
'normal.keydown': function(e) {
if (this.getStatus() == 'textedit') return;
if (e.originEvent.keyCode == keymap['/']) {
var node = this.getSelectedNode();
if (!node || node == this.getRoot()) return;
var expanded = node.isExpanded();
this.getSelectedNodes().forEach(function(node) {
if (expanded) node.collapse();
else node.expand();
node.renderTree();
});
this.layout(100);
this.fire('contentchange');
e.preventDefault();
e.stopPropagationImmediately();
}
if (e.isShortcutKey('Alt+`')) {
this.execCommand('expandtolevel', 9999);
}
for (var i = 1; i < 6; i++) {
if (e.isShortcutKey('Alt+' + i)) {
this.execCommand('expandtolevel', i);
}
}
}
},
renderers: {
outside: ExpanderRenderer
},
contextmenu: [{
command: 'expandtoleaf',
query: function() {
return !minder.getSelectedNode();
},
fn: function(minder) {
minder.execCommand('expandtolevel', 9999);
}
}, {
command: 'expandtolevel1',
query: function() {
return !minder.getSelectedNode();
},
fn: function(minder) {
minder.execCommand('expandtolevel', 1);
}
}, {
command: 'expandtolevel2',
query: function() {
return !minder.getSelectedNode();
},
fn: function(minder) {
minder.execCommand('expandtolevel', 2);
}
},{
command: 'expandtolevel3',
query: function() {
return !minder.getSelectedNode();
},
fn: function(minder) {
minder.execCommand('expandtolevel', 3);
}
}, {
divider: true
}]
};
});
});