425 lines
12 KiB
JavaScript
425 lines
12 KiB
JavaScript
|
|
||
|
/*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||
|
* or more contributor license agreements. See the NOTICE file
|
||
|
* distributed with this work for additional information
|
||
|
* regarding copyright ownership. The ASF licenses this file
|
||
|
* to you under the Apache License, Version 2.0 (the
|
||
|
* "License"); you may not use this file except in compliance
|
||
|
* with the License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing,
|
||
|
* software distributed under the License is distributed on an
|
||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
* KIND, either express or implied. See the License for the
|
||
|
* specific language governing permissions and limitations
|
||
|
* under the License.
|
||
|
*/
|
||
|
|
||
|
var zrUtil = require("zrender/lib/core/util");
|
||
|
|
||
|
var SeriesModel = require("../../model/Series");
|
||
|
|
||
|
var Tree = require("../../data/Tree");
|
||
|
|
||
|
var Model = require("../../model/Model");
|
||
|
|
||
|
var _format = require("../../util/format");
|
||
|
|
||
|
var encodeHTML = _format.encodeHTML;
|
||
|
var addCommas = _format.addCommas;
|
||
|
|
||
|
var _treeHelper = require("../helper/treeHelper");
|
||
|
|
||
|
var wrapTreePathInfo = _treeHelper.wrapTreePathInfo;
|
||
|
|
||
|
/*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||
|
* or more contributor license agreements. See the NOTICE file
|
||
|
* distributed with this work for additional information
|
||
|
* regarding copyright ownership. The ASF licenses this file
|
||
|
* to you under the Apache License, Version 2.0 (the
|
||
|
* "License"); you may not use this file except in compliance
|
||
|
* with the License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing,
|
||
|
* software distributed under the License is distributed on an
|
||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
* KIND, either express or implied. See the License for the
|
||
|
* specific language governing permissions and limitations
|
||
|
* under the License.
|
||
|
*/
|
||
|
var _default = SeriesModel.extend({
|
||
|
type: 'series.treemap',
|
||
|
layoutMode: 'box',
|
||
|
dependencies: ['grid', 'polar'],
|
||
|
preventUsingHoverLayer: true,
|
||
|
|
||
|
/**
|
||
|
* @type {module:echarts/data/Tree~Node}
|
||
|
*/
|
||
|
_viewRoot: null,
|
||
|
defaultOption: {
|
||
|
// Disable progressive rendering
|
||
|
progressive: 0,
|
||
|
// center: ['50%', '50%'], // not supported in ec3.
|
||
|
// size: ['80%', '80%'], // deprecated, compatible with ec2.
|
||
|
left: 'center',
|
||
|
top: 'middle',
|
||
|
right: null,
|
||
|
bottom: null,
|
||
|
width: '80%',
|
||
|
height: '80%',
|
||
|
sort: true,
|
||
|
// Can be null or false or true
|
||
|
// (order by desc default, asc not supported yet (strange effect))
|
||
|
clipWindow: 'origin',
|
||
|
// Size of clipped window when zooming. 'origin' or 'fullscreen'
|
||
|
squareRatio: 0.5 * (1 + Math.sqrt(5)),
|
||
|
// golden ratio
|
||
|
leafDepth: null,
|
||
|
// Nodes on depth from root are regarded as leaves.
|
||
|
// Count from zero (zero represents only view root).
|
||
|
drillDownIcon: '▶',
|
||
|
// Use html character temporarily because it is complicated
|
||
|
// to align specialized icon. ▷▶❒❐▼✚
|
||
|
zoomToNodeRatio: 0.32 * 0.32,
|
||
|
// Be effective when using zoomToNode. Specify the proportion of the
|
||
|
// target node area in the view area.
|
||
|
roam: true,
|
||
|
// true, false, 'scale' or 'zoom', 'move'.
|
||
|
nodeClick: 'zoomToNode',
|
||
|
// Leaf node click behaviour: 'zoomToNode', 'link', false.
|
||
|
// If leafDepth is set and clicking a node which has children but
|
||
|
// be on left depth, the behaviour would be changing root. Otherwise
|
||
|
// use behavious defined above.
|
||
|
animation: true,
|
||
|
animationDurationUpdate: 900,
|
||
|
animationEasing: 'quinticInOut',
|
||
|
breadcrumb: {
|
||
|
show: true,
|
||
|
height: 22,
|
||
|
left: 'center',
|
||
|
top: 'bottom',
|
||
|
// right
|
||
|
// bottom
|
||
|
emptyItemWidth: 25,
|
||
|
// Width of empty node.
|
||
|
itemStyle: {
|
||
|
color: 'rgba(0,0,0,0.7)',
|
||
|
//'#5793f3',
|
||
|
borderColor: 'rgba(255,255,255,0.7)',
|
||
|
borderWidth: 1,
|
||
|
shadowColor: 'rgba(150,150,150,1)',
|
||
|
shadowBlur: 3,
|
||
|
shadowOffsetX: 0,
|
||
|
shadowOffsetY: 0,
|
||
|
textStyle: {
|
||
|
color: '#fff'
|
||
|
}
|
||
|
},
|
||
|
emphasis: {
|
||
|
textStyle: {}
|
||
|
}
|
||
|
},
|
||
|
label: {
|
||
|
show: true,
|
||
|
// Do not use textDistance, for ellipsis rect just the same as treemap node rect.
|
||
|
distance: 0,
|
||
|
padding: 5,
|
||
|
position: 'inside',
|
||
|
// Can be [5, '5%'] or position stirng like 'insideTopLeft', ...
|
||
|
// formatter: null,
|
||
|
color: '#fff',
|
||
|
ellipsis: true // align
|
||
|
// verticalAlign
|
||
|
|
||
|
},
|
||
|
upperLabel: {
|
||
|
// Label when node is parent.
|
||
|
show: false,
|
||
|
position: [0, '50%'],
|
||
|
height: 20,
|
||
|
// formatter: null,
|
||
|
color: '#fff',
|
||
|
ellipsis: true,
|
||
|
// align: null,
|
||
|
verticalAlign: 'middle'
|
||
|
},
|
||
|
itemStyle: {
|
||
|
color: null,
|
||
|
// Can be 'none' if not necessary.
|
||
|
colorAlpha: null,
|
||
|
// Can be 'none' if not necessary.
|
||
|
colorSaturation: null,
|
||
|
// Can be 'none' if not necessary.
|
||
|
borderWidth: 0,
|
||
|
gapWidth: 0,
|
||
|
borderColor: '#fff',
|
||
|
borderColorSaturation: null // If specified, borderColor will be ineffective, and the
|
||
|
// border color is evaluated by color of current node and
|
||
|
// borderColorSaturation.
|
||
|
|
||
|
},
|
||
|
emphasis: {
|
||
|
upperLabel: {
|
||
|
show: true,
|
||
|
position: [0, '50%'],
|
||
|
color: '#fff',
|
||
|
ellipsis: true,
|
||
|
verticalAlign: 'middle'
|
||
|
}
|
||
|
},
|
||
|
visualDimension: 0,
|
||
|
// Can be 0, 1, 2, 3.
|
||
|
visualMin: null,
|
||
|
visualMax: null,
|
||
|
color: [],
|
||
|
// + treemapSeries.color should not be modified. Please only modified
|
||
|
// level[n].color (if necessary).
|
||
|
// + Specify color list of each level. level[0].color would be global
|
||
|
// color list if not specified. (see method `setDefault`).
|
||
|
// + But set as a empty array to forbid fetch color from global palette
|
||
|
// when using nodeModel.get('color'), otherwise nodes on deep level
|
||
|
// will always has color palette set and are not able to inherit color
|
||
|
// from parent node.
|
||
|
// + TreemapSeries.color can not be set as 'none', otherwise effect
|
||
|
// legend color fetching (see seriesColor.js).
|
||
|
colorAlpha: null,
|
||
|
// Array. Specify color alpha range of each level, like [0.2, 0.8]
|
||
|
colorSaturation: null,
|
||
|
// Array. Specify color saturation of each level, like [0.2, 0.5]
|
||
|
colorMappingBy: 'index',
|
||
|
// 'value' or 'index' or 'id'.
|
||
|
visibleMin: 10,
|
||
|
// If area less than this threshold (unit: pixel^2), node will not
|
||
|
// be rendered. Only works when sort is 'asc' or 'desc'.
|
||
|
childrenVisibleMin: null,
|
||
|
// If area of a node less than this threshold (unit: pixel^2),
|
||
|
// grandchildren will not show.
|
||
|
// Why grandchildren? If not grandchildren but children,
|
||
|
// some siblings show children and some not,
|
||
|
// the appearance may be mess and not consistent,
|
||
|
levels: [] // Each item: {
|
||
|
// visibleMin, itemStyle, visualDimension, label
|
||
|
// }
|
||
|
// data: {
|
||
|
// value: [],
|
||
|
// children: [],
|
||
|
// link: 'http://xxx.xxx.xxx',
|
||
|
// target: 'blank' or 'self'
|
||
|
// }
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @override
|
||
|
*/
|
||
|
getInitialData: function (option, ecModel) {
|
||
|
// Create a virtual root.
|
||
|
var root = {
|
||
|
name: option.name,
|
||
|
children: option.data
|
||
|
};
|
||
|
completeTreeValue(root);
|
||
|
var levels = option.levels || []; // Used in "visual priority" in `treemapVisual.js`.
|
||
|
// This way is a little tricky, must satisfy the precondition:
|
||
|
// 1. There is no `treeNode.getModel('itemStyle.xxx')` used.
|
||
|
// 2. The `Model.prototype.getModel()` will not use any clone-like way.
|
||
|
|
||
|
var designatedVisualItemStyle = this.designatedVisualItemStyle = {};
|
||
|
var designatedVisualModel = new Model({
|
||
|
itemStyle: designatedVisualItemStyle
|
||
|
}, this, ecModel);
|
||
|
levels = option.levels = setDefault(levels, ecModel);
|
||
|
var levelModels = zrUtil.map(levels || [], function (levelDefine) {
|
||
|
return new Model(levelDefine, designatedVisualModel, ecModel);
|
||
|
}, this); // Make sure always a new tree is created when setOption,
|
||
|
// in TreemapView, we check whether oldTree === newTree
|
||
|
// to choose mappings approach among old shapes and new shapes.
|
||
|
|
||
|
var tree = Tree.createTree(root, this, beforeLink);
|
||
|
|
||
|
function beforeLink(nodeData) {
|
||
|
nodeData.wrapMethod('getItemModel', function (model, idx) {
|
||
|
var node = tree.getNodeByDataIndex(idx);
|
||
|
var levelModel = levelModels[node.depth]; // If no levelModel, we also need `designatedVisualModel`.
|
||
|
|
||
|
model.parentModel = levelModel || designatedVisualModel;
|
||
|
return model;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return tree.data;
|
||
|
},
|
||
|
optionUpdated: function () {
|
||
|
this.resetViewRoot();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @override
|
||
|
* @param {number} dataIndex
|
||
|
* @param {boolean} [mutipleSeries=false]
|
||
|
*/
|
||
|
formatTooltip: function (dataIndex) {
|
||
|
var data = this.getData();
|
||
|
var value = this.getRawValue(dataIndex);
|
||
|
var formattedValue = zrUtil.isArray(value) ? addCommas(value[0]) : addCommas(value);
|
||
|
var name = data.getName(dataIndex);
|
||
|
return encodeHTML(name + ': ' + formattedValue);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Add tree path to tooltip param
|
||
|
*
|
||
|
* @override
|
||
|
* @param {number} dataIndex
|
||
|
* @return {Object}
|
||
|
*/
|
||
|
getDataParams: function (dataIndex) {
|
||
|
var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
|
||
|
var node = this.getData().tree.getNodeByDataIndex(dataIndex);
|
||
|
params.treePathInfo = wrapTreePathInfo(node, this);
|
||
|
return params;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @public
|
||
|
* @param {Object} layoutInfo {
|
||
|
* x: containerGroup x
|
||
|
* y: containerGroup y
|
||
|
* width: containerGroup width
|
||
|
* height: containerGroup height
|
||
|
* }
|
||
|
*/
|
||
|
setLayoutInfo: function (layoutInfo) {
|
||
|
/**
|
||
|
* @readOnly
|
||
|
* @type {Object}
|
||
|
*/
|
||
|
this.layoutInfo = this.layoutInfo || {};
|
||
|
zrUtil.extend(this.layoutInfo, layoutInfo);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @param {string} id
|
||
|
* @return {number} index
|
||
|
*/
|
||
|
mapIdToIndex: function (id) {
|
||
|
// A feature is implemented:
|
||
|
// index is monotone increasing with the sequence of
|
||
|
// input id at the first time.
|
||
|
// This feature can make sure that each data item and its
|
||
|
// mapped color have the same index between data list and
|
||
|
// color list at the beginning, which is useful for user
|
||
|
// to adjust data-color mapping.
|
||
|
|
||
|
/**
|
||
|
* @private
|
||
|
* @type {Object}
|
||
|
*/
|
||
|
var idIndexMap = this._idIndexMap;
|
||
|
|
||
|
if (!idIndexMap) {
|
||
|
idIndexMap = this._idIndexMap = zrUtil.createHashMap();
|
||
|
/**
|
||
|
* @private
|
||
|
* @type {number}
|
||
|
*/
|
||
|
|
||
|
this._idIndexMapCount = 0;
|
||
|
}
|
||
|
|
||
|
var index = idIndexMap.get(id);
|
||
|
|
||
|
if (index == null) {
|
||
|
idIndexMap.set(id, index = this._idIndexMapCount++);
|
||
|
}
|
||
|
|
||
|
return index;
|
||
|
},
|
||
|
getViewRoot: function () {
|
||
|
return this._viewRoot;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @param {module:echarts/data/Tree~Node} [viewRoot]
|
||
|
*/
|
||
|
resetViewRoot: function (viewRoot) {
|
||
|
viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot;
|
||
|
var root = this.getRawData().tree.root;
|
||
|
|
||
|
if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) {
|
||
|
this._viewRoot = root;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
/**
|
||
|
* @param {Object} dataNode
|
||
|
*/
|
||
|
|
||
|
|
||
|
function completeTreeValue(dataNode) {
|
||
|
// Postorder travel tree.
|
||
|
// If value of none-leaf node is not set,
|
||
|
// calculate it by suming up the value of all children.
|
||
|
var sum = 0;
|
||
|
zrUtil.each(dataNode.children, function (child) {
|
||
|
completeTreeValue(child);
|
||
|
var childValue = child.value;
|
||
|
zrUtil.isArray(childValue) && (childValue = childValue[0]);
|
||
|
sum += childValue;
|
||
|
});
|
||
|
var thisValue = dataNode.value;
|
||
|
|
||
|
if (zrUtil.isArray(thisValue)) {
|
||
|
thisValue = thisValue[0];
|
||
|
}
|
||
|
|
||
|
if (thisValue == null || isNaN(thisValue)) {
|
||
|
thisValue = sum;
|
||
|
} // Value should not less than 0.
|
||
|
|
||
|
|
||
|
if (thisValue < 0) {
|
||
|
thisValue = 0;
|
||
|
}
|
||
|
|
||
|
zrUtil.isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue;
|
||
|
}
|
||
|
/**
|
||
|
* set default to level configuration
|
||
|
*/
|
||
|
|
||
|
|
||
|
function setDefault(levels, ecModel) {
|
||
|
var globalColorList = ecModel.get('color');
|
||
|
|
||
|
if (!globalColorList) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
levels = levels || [];
|
||
|
var hasColorDefine;
|
||
|
zrUtil.each(levels, function (levelDefine) {
|
||
|
var model = new Model(levelDefine);
|
||
|
var modelColor = model.get('color');
|
||
|
|
||
|
if (model.get('itemStyle.color') || modelColor && modelColor !== 'none') {
|
||
|
hasColorDefine = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (!hasColorDefine) {
|
||
|
var level0 = levels[0] || (levels[0] = {});
|
||
|
level0.color = globalColorList.slice();
|
||
|
}
|
||
|
|
||
|
return levels;
|
||
|
}
|
||
|
|
||
|
module.exports = _default;
|