This is an automated email from the ASF dual-hosted git repository.
shenyi pushed a commit to branch typescript
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
The following commit(s) were added to refs/heads/typescript by this push:
new ab16b5a ts: add types for Graph and Tree data structure
ab16b5a is described below
commit ab16b5ae8d0b4bdb7633dfb1a2011650c24ebdaf
Author: pissang <[email protected]>
AuthorDate: Wed Feb 19 22:47:56 2020 +0800
ts: add types for Graph and Tree data structure
---
src/data/Graph.ts | 706 ++++++++++++++++--------------------
src/data/Tree.ts | 490 +++++++++++--------------
src/data/helper/createDimensions.ts | 3 +-
src/model/Model.ts | 4 +-
4 files changed, 532 insertions(+), 671 deletions(-)
diff --git a/src/data/Graph.ts b/src/data/Graph.ts
index b963d1b..c78536c 100644
--- a/src/data/Graph.ts
+++ b/src/data/Graph.ts
@@ -17,522 +17,460 @@
* under the License.
*/
-// @ts-nocheck
-
import {__DEV__} from '../config';
import * as zrUtil from 'zrender/src/core/util';
-import {enableClassCheck} from '../util/clazz';
+import { Dictionary } from 'zrender/src/core/types';
+import List from './List';
+import Model from '../model/Model';
+import Element from 'zrender/src/Element';
+import { DimensionLoose, ParsedDataValue } from '../util/types';
// id may be function name of Object, add a prefix to avoid this problem.
-function generateNodeKey(id) {
+function generateNodeKey(id: string): string {
return '_EC_' + id;
}
-/**
- * @alias module:echarts/data/Graph
- * @constructor
- * @param {boolean} directed
- */
-var Graph = function (directed) {
+
+class Graph {
+ type: 'graph' = 'graph'
+
+ readonly nodes: GraphNode[] = [];
+
+ readonly edges: GraphEdge[] = [];
+
+ data: List
+
+ edgeData: List
+
/**
* 是否是有向图
- * @type {boolean}
- * @private
*/
- this._directed = directed || false;
+ private _directed: boolean
+ private _nodesMap: Dictionary<GraphNode> = {};
/**
- * @type {Array.<module:echarts/data/Graph.Node>}
- * @readOnly
+ * @type {Object.<string, module:echarts/data/Graph.Edge>}
+ * @private
*/
- this.nodes = [];
+ private _edgesMap: Dictionary<GraphEdge> = {};
+
+
+ constructor(directed?: boolean) {
+ this._directed = directed || false;
+ }
/**
- * @type {Array.<module:echarts/data/Graph.Edge>}
- * @readOnly
+ * If is directed graph
*/
- this.edges = [];
+ isDirected(): boolean {
+ return this._directed;
+ };
/**
- * @type {Object.<string, module:echarts/data/Graph.Node>}
- * @private
+ * Add a new node
*/
- this._nodesMap = {};
+ addNode(id: string, dataIndex?: number): GraphNode {
+ id = id == null ? ('' + dataIndex) : ('' + id);
+
+ var nodesMap = this._nodesMap;
+
+ if (nodesMap[generateNodeKey(id)]) {
+ if (__DEV__) {
+ console.error('Graph nodes have duplicate name or id');
+ }
+ return;
+ }
+
+ var node = new GraphNode(id, dataIndex);
+ node.hostGraph = this;
+
+ this.nodes.push(node);
+
+ nodesMap[generateNodeKey(id)] = node;
+ return node;
+ };
+
/**
- * @type {Object.<string, module:echarts/data/Graph.Edge>}
- * @private
+ * Get node by data index
*/
- this._edgesMap = {};
-
+ getNodeByIndex(dataIndex: number): GraphNode {
+ var rawIdx = this.data.getRawIndex(dataIndex);
+ return this.nodes[rawIdx];
+ };
/**
- * @type {module:echarts/data/List}
- * @readOnly
+ * Get node by id
*/
- this.data;
+ getNodeById(id: string): GraphNode {
+ return this._nodesMap[generateNodeKey(id)];
+ };
/**
- * @type {module:echarts/data/List}
- * @readOnly
+ * Add a new edge
*/
- this.edgeData;
-};
-
-var graphProto = Graph.prototype;
-/**
- * @type {string}
- */
-graphProto.type = 'graph';
-
-/**
- * If is directed graph
- * @return {boolean}
- */
-graphProto.isDirected = function () {
- return this._directed;
-};
-
-/**
- * Add a new node
- * @param {string} id
- * @param {number} [dataIndex]
- */
-graphProto.addNode = function (id, dataIndex) {
- id = id == null ? ('' + dataIndex) : ('' + id);
+ addEdge(n1: GraphNode | number | string, n2: GraphNode | number | string,
dataIndex?: number) {
+ var nodesMap = this._nodesMap;
+ var edgesMap = this._edgesMap;
- var nodesMap = this._nodesMap;
-
- if (nodesMap[generateNodeKey(id)]) {
- if (__DEV__) {
- console.error('Graph nodes have duplicate name or id');
+ // PNEDING
+ if (typeof n1 === 'number') {
+ n1 = this.nodes[n1];
+ }
+ if (typeof n2 === 'number') {
+ n2 = this.nodes[n2];
}
- return;
- }
- var node = new Node(id, dataIndex);
- node.hostGraph = this;
+ if (!(n1 instanceof GraphNode)) {
+ n1 = nodesMap[generateNodeKey(n1)];
+ }
+ if (!(n2 instanceof GraphNode)) {
+ n2 = nodesMap[generateNodeKey(n2)];
+ }
+ if (!n1 || !n2) {
+ return;
+ }
- this.nodes.push(node);
+ var key = n1.id + '-' + n2.id;
+ // PENDING
+ if (edgesMap[key]) {
+ return;
+ }
- nodesMap[generateNodeKey(id)] = node;
- return node;
-};
+ var edge = new GraphEdge(n1, n2, dataIndex);
+ edge.hostGraph = this;
-/**
- * Get node by data index
- * @param {number} dataIndex
- * @return {module:echarts/data/Graph~Node}
- */
-graphProto.getNodeByIndex = function (dataIndex) {
- var rawIdx = this.data.getRawIndex(dataIndex);
- return this.nodes[rawIdx];
-};
-/**
- * Get node by id
- * @param {string} id
- * @return {module:echarts/data/Graph.Node}
- */
-graphProto.getNodeById = function (id) {
- return this._nodesMap[generateNodeKey(id)];
-};
+ if (this._directed) {
+ n1.outEdges.push(edge);
+ n2.inEdges.push(edge);
+ }
+ n1.edges.push(edge);
+ if (n1 !== n2) {
+ n2.edges.push(edge);
+ }
-/**
- * Add a new edge
- * @param {number|string|module:echarts/data/Graph.Node} n1
- * @param {number|string|module:echarts/data/Graph.Node} n2
- * @param {number} [dataIndex=-1]
- * @return {module:echarts/data/Graph.Edge}
- */
-graphProto.addEdge = function (n1, n2, dataIndex) {
- var nodesMap = this._nodesMap;
- var edgesMap = this._edgesMap;
-
- // PNEDING
- if (typeof n1 === 'number') {
- n1 = this.nodes[n1];
- }
- if (typeof n2 === 'number') {
- n2 = this.nodes[n2];
- }
+ this.edges.push(edge);
+ edgesMap[key] = edge;
- if (!Node.isInstance(n1)) {
- n1 = nodesMap[generateNodeKey(n1)];
- }
- if (!Node.isInstance(n2)) {
- n2 = nodesMap[generateNodeKey(n2)];
- }
- if (!n1 || !n2) {
- return;
- }
+ return edge;
+ };
- var key = n1.id + '-' + n2.id;
- // PENDING
- if (edgesMap[key]) {
- return;
- }
+ /**
+ * Get edge by data index
+ */
+ getEdgeByIndex(dataIndex: number): GraphEdge {
+ var rawIdx = this.edgeData.getRawIndex(dataIndex);
+ return this.edges[rawIdx];
+ };
+ /**
+ * Get edge by two linked nodes
+ */
+ getEdge(n1: string | GraphNode, n2: string | GraphNode): GraphEdge {
+ if (n1 instanceof GraphNode) {
+ n1 = n1.id;
+ }
+ if (n2 instanceof GraphNode) {
+ n2 = n2.id;
+ }
- var edge = new Edge(n1, n2, dataIndex);
- edge.hostGraph = this;
+ var edgesMap = this._edgesMap;
- if (this._directed) {
- n1.outEdges.push(edge);
- n2.inEdges.push(edge);
- }
- n1.edges.push(edge);
- if (n1 !== n2) {
- n2.edges.push(edge);
- }
+ if (this._directed) {
+ return edgesMap[n1 + '-' + n2];
+ }
+ else {
+ return edgesMap[n1 + '-' + n2]
+ || edgesMap[n2 + '-' + n1];
+ }
+ };
- this.edges.push(edge);
- edgesMap[key] = edge;
+ /**
+ * Iterate all nodes
+ */
+ eachNode<Ctx>(
+ cb: (this: Ctx, node: GraphNode, idx: number) => void,
+ context?: Ctx
+ ) {
+ var nodes = this.nodes;
+ var len = nodes.length;
+ for (var i = 0; i < len; i++) {
+ if (nodes[i].dataIndex >= 0) {
+ cb.call(context, nodes[i], i);
+ }
+ }
+ };
- return edge;
-};
+ /**
+ * Iterate all edges
+ */
+ eachEdge<Ctx>(
+ cb: (this: Ctx, edge: GraphEdge, idx: number) => void,
+ context?: Ctx
+ ) {
+ var edges = this.edges;
+ var len = edges.length;
+ for (var i = 0; i < len; i++) {
+ if (edges[i].dataIndex >= 0
+ && edges[i].node1.dataIndex >= 0
+ && edges[i].node2.dataIndex >= 0
+ ) {
+ cb.call(context, edges[i], i);
+ }
+ }
+ };
-/**
- * Get edge by data index
- * @param {number} dataIndex
- * @return {module:echarts/data/Graph~Node}
- */
-graphProto.getEdgeByIndex = function (dataIndex) {
- var rawIdx = this.edgeData.getRawIndex(dataIndex);
- return this.edges[rawIdx];
-};
-/**
- * Get edge by two linked nodes
- * @param {module:echarts/data/Graph.Node|string} n1
- * @param {module:echarts/data/Graph.Node|string} n2
- * @return {module:echarts/data/Graph.Edge}
- */
-graphProto.getEdge = function (n1, n2) {
- if (Node.isInstance(n1)) {
- n1 = n1.id;
- }
- if (Node.isInstance(n2)) {
- n2 = n2.id;
- }
+ /**
+ * Breadth first traverse
+ * Return true to stop traversing
+ */
+ breadthFirstTraverse<Ctx>(
+ cb: (this: Ctx, node: GraphNode, fromNode: GraphNode) => boolean,
+ startNode: GraphNode | string,
+ direction: 'none' | 'in' | 'out',
+ context?: Ctx
+ ) {
+ if (!(startNode instanceof GraphNode)) {
+ startNode = this._nodesMap[generateNodeKey(startNode)];
+ }
+ if (!startNode) {
+ return;
+ }
- var edgesMap = this._edgesMap;
+ var edgeType: 'inEdges' | 'outEdges' | 'edges' = direction === 'out'
+ ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges');
- if (this._directed) {
- return edgesMap[n1 + '-' + n2];
- }
- else {
- return edgesMap[n1 + '-' + n2]
- || edgesMap[n2 + '-' + n1];
- }
-};
+ for (var i = 0; i < this.nodes.length; i++) {
+ this.nodes[i].__visited = false;
+ }
-/**
- * Iterate all nodes
- * @param {Function} cb
- * @param {*} [context]
- */
-graphProto.eachNode = function (cb, context) {
- var nodes = this.nodes;
- var len = nodes.length;
- for (var i = 0; i < len; i++) {
- if (nodes[i].dataIndex >= 0) {
- cb.call(context, nodes[i], i);
+ if (cb.call(context, startNode, null)) {
+ return;
}
- }
-};
-/**
- * Iterate all edges
- * @param {Function} cb
- * @param {*} [context]
- */
-graphProto.eachEdge = function (cb, context) {
- var edges = this.edges;
- var len = edges.length;
- for (var i = 0; i < len; i++) {
- if (edges[i].dataIndex >= 0
- && edges[i].node1.dataIndex >= 0
- && edges[i].node2.dataIndex >= 0
- ) {
- cb.call(context, edges[i], i);
+ var queue = [startNode];
+ while (queue.length) {
+ var currentNode = queue.shift();
+ var edges = currentNode[edgeType];
+
+ for (var i = 0; i < edges.length; i++) {
+ var e = edges[i];
+ var otherNode = e.node1 === currentNode
+ ? e.node2 : e.node1;
+ if (!otherNode.__visited) {
+ if (cb.call(context, otherNode, currentNode)) {
+ // Stop traversing
+ return;
+ }
+ queue.push(otherNode);
+ otherNode.__visited = true;
+ }
+ }
}
- }
-};
+ };
-/**
- * Breadth first traverse
- * @param {Function} cb
- * @param {module:echarts/data/Graph.Node} startNode
- * @param {string} [direction='none'] 'none'|'in'|'out'
- * @param {*} [context]
- */
-graphProto.breadthFirstTraverse = function (
- cb, startNode, direction, context
-) {
- if (!Node.isInstance(startNode)) {
- startNode = this._nodesMap[generateNodeKey(startNode)];
- }
- if (!startNode) {
- return;
- }
+ // TODO
+ // depthFirstTraverse(
+ // cb, startNode, direction, context
+ // ) {
- var edgeType = direction === 'out'
- ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges');
+ // };
- for (var i = 0; i < this.nodes.length; i++) {
- this.nodes[i].__visited = false;
- }
+ // Filter update
+ update() {
+ var data = this.data;
+ var edgeData = this.edgeData;
+ var nodes = this.nodes;
+ var edges = this.edges;
- if (cb.call(context, startNode, null)) {
- return;
- }
+ for (var i = 0, len = nodes.length; i < len; i++) {
+ nodes[i].dataIndex = -1;
+ }
+ for (var i = 0, len = data.count(); i < len; i++) {
+ nodes[data.getRawIndex(i)].dataIndex = i;
+ }
- var queue = [startNode];
- while (queue.length) {
- var currentNode = queue.shift();
- var edges = currentNode[edgeType];
+ edgeData.filterSelf(function (idx) {
+ var edge = edges[edgeData.getRawIndex(idx)];
+ return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;
+ });
+ // Update edge
+ for (var i = 0, len = edges.length; i < len; i++) {
+ edges[i].dataIndex = -1;
+ }
+ for (var i = 0, len = edgeData.count(); i < len; i++) {
+ edges[edgeData.getRawIndex(i)].dataIndex = i;
+ }
+ };
+
+ /**
+ * @return {module:echarts/data/Graph}
+ */
+ clone() {
+ var graph = new Graph(this._directed);
+ var nodes = this.nodes;
+ var edges = this.edges;
+ for (var i = 0; i < nodes.length; i++) {
+ graph.addNode(nodes[i].id, nodes[i].dataIndex);
+ }
for (var i = 0; i < edges.length; i++) {
var e = edges[i];
- var otherNode = e.node1 === currentNode
- ? e.node2 : e.node1;
- if (!otherNode.__visited) {
- if (cb.call(context, otherNode, currentNode)) {
- // Stop traversing
- return;
- }
- queue.push(otherNode);
- otherNode.__visited = true;
- }
+ graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);
}
- }
-};
+ return graph;
+ };
-// TODO
-// graphProto.depthFirstTraverse = function (
-// cb, startNode, direction, context
-// ) {
+ static Node: typeof GraphNode
+ static Edge: typeof GraphEdge
-// };
+}
-// Filter update
-graphProto.update = function () {
- var data = this.data;
- var edgeData = this.edgeData;
- var nodes = this.nodes;
- var edges = this.edges;
- for (var i = 0, len = nodes.length; i < len; i++) {
- nodes[i].dataIndex = -1;
- }
- for (var i = 0, len = data.count(); i < len; i++) {
- nodes[data.getRawIndex(i)].dataIndex = i;
- }
+class GraphNode {
- edgeData.filterSelf(function (idx) {
- var edge = edges[edgeData.getRawIndex(idx)];
- return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;
- });
+ id: string
- // Update edge
- for (var i = 0, len = edges.length; i < len; i++) {
- edges[i].dataIndex = -1;
- }
- for (var i = 0, len = edgeData.count(); i < len; i++) {
- edges[edgeData.getRawIndex(i)].dataIndex = i;
- }
-};
+ inEdges: GraphEdge[] = []
-/**
- * @return {module:echarts/data/Graph}
- */
-graphProto.clone = function () {
- var graph = new Graph(this._directed);
- var nodes = this.nodes;
- var edges = this.edges;
- for (var i = 0; i < nodes.length; i++) {
- graph.addNode(nodes[i].id, nodes[i].dataIndex);
- }
- for (var i = 0; i < edges.length; i++) {
- var e = edges[i];
- graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);
- }
- return graph;
-};
+ outEdges: GraphEdge[] = []
+ edges: GraphEdge[] = [];
-/**
- * @alias module:echarts/data/Graph.Node
- */
-function Node(id, dataIndex) {
- /**
- * @type {string}
- */
- this.id = id == null ? '' : id;
+ hostGraph: Graph
- /**
- * @type {Array.<module:echarts/data/Graph.Edge>}
- */
- this.inEdges = [];
- /**
- * @type {Array.<module:echarts/data/Graph.Edge>}
- */
- this.outEdges = [];
- /**
- * @type {Array.<module:echarts/data/Graph.Edge>}
- */
- this.edges = [];
- /**
- * @type {module:echarts/data/Graph}
- */
- this.hostGraph;
+ dataIndex: number = -1
- /**
- * @type {number}
- */
- this.dataIndex = dataIndex == null ? -1 : dataIndex;
-}
+ // Used in traverse of Graph
+ __visited: boolean
+
+ constructor(id?: string, dataIndex?: number) {
+ this.id = id == null ? '' : id;
-Node.prototype = {
+ this.inEdges = [];
- constructor: Node,
+ this.outEdges = [];
+
+ this.edges = [];
+
+ this.hostGraph;
+
+ this.dataIndex = dataIndex == null ? -1 : dataIndex;
+ }
/**
* @return {number}
*/
- degree: function () {
+ degree() {
return this.edges.length;
- },
+ }
/**
* @return {number}
*/
- inDegree: function () {
+ inDegree() {
return this.inEdges.length;
- },
+ }
/**
* @return {number}
*/
- outDegree: function () {
+ outDegree() {
return this.outEdges.length;
- },
+ }
- /**
- * @param {string} [path]
- * @return {module:echarts/model/Model}
- */
- getModel: function (path) {
+ // TODO: TYPE Same type with Model#getModel
+ getModel(path: string | string[]): Model {
if (this.dataIndex < 0) {
return;
}
var graph = this.hostGraph;
var itemModel = graph.data.getItemModel(this.dataIndex);
- return itemModel.getModel(path);
+ return itemModel.getModel(path as [string]);
}
-};
+}
-/**
- * 图边
- * @alias module:echarts/data/Graph.Edge
- * @param {module:echarts/data/Graph.Node} n1
- * @param {module:echarts/data/Graph.Node} n2
- * @param {number} [dataIndex=-1]
- */
-function Edge(n1, n2, dataIndex) {
+class GraphEdge {
/**
* 节点1,如果是有向图则为源节点
- * @type {module:echarts/data/Graph.Node}
*/
- this.node1 = n1;
-
+ node1: GraphNode
/**
* 节点2,如果是有向图则为目标节点
- * @type {module:echarts/data/Graph.Node}
*/
- this.node2 = n2;
+ node2: GraphNode
- this.dataIndex = dataIndex == null ? -1 : dataIndex;
-}
+ dataIndex: number = -1
+
+ hostGraph: Graph
-/**
- * @param {string} [path]
- * @return {module:echarts/model/Model}
- */
-Edge.prototype.getModel = function (path) {
- if (this.dataIndex < 0) {
- return;
+ constructor(n1: GraphNode, n2: GraphNode, dataIndex?: number) {
+ this.node1 = n1;
+ this.node2 = n2;
+ this.dataIndex = dataIndex == null ? -1 : dataIndex;
}
- var graph = this.hostGraph;
- var itemModel = graph.edgeData.getItemModel(this.dataIndex);
- return itemModel.getModel(path);
-};
+ getModel(path: string | string[]): Model {
+ if (this.dataIndex < 0) {
+ return;
+ }
+ var graph = this.hostGraph;
+ var itemModel = graph.edgeData.getItemModel(this.dataIndex);
+
+ return itemModel.getModel(path as [string]);
+ }
+}
-var createGraphDataProxyMixin = function (hostName, dataName) {
+type GetDataName<Host> = Host extends GraphEdge ? 'edgeData' : 'data';
+
+function createGraphDataProxyMixin<Host extends GraphEdge | GraphNode>(
+ hostName: 'hostGraph',
+ dataName: GetDataName<Host>
+) {
return {
/**
- * @param {string=} [dimension='value'] Default 'value'. can be 'a',
'b', 'c', 'd', 'e'.
- * @return {number}
+ * @param Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.
*/
- getValue: function (dimension) {
+ getValue: function (this: Host, dimension: DimensionLoose):
ParsedDataValue {
var data = this[hostName][dataName];
return data.get(data.getDimension(dimension || 'value'),
this.dataIndex);
},
- /**
- * @param {Object|string} key
- * @param {*} [value]
- */
- setVisual: function (key, value) {
+ setVisual: function (this: Host, key: string | Dictionary<any>,
value?: any) {
this.dataIndex >= 0
- && this[hostName][dataName].setItemVisual(this.dataIndex, key,
value);
+ && this[hostName][dataName].setItemVisual(this.dataIndex, key
as string, value);
},
- /**
- * @param {string} key
- * @return {boolean}
- */
- getVisual: function (key, ignoreParent) {
+ getVisual: function (this: Host, key: string, ignoreParent?: boolean) {
return this[hostName][dataName].getItemVisual(this.dataIndex, key,
ignoreParent);
},
- /**
- * @param {Object} layout
- * @return {boolean} [merge=false]
- */
- setLayout: function (layout, merge) {
+ setLayout: function (this: Host, layout: any, merge?: boolean) {
this.dataIndex >= 0
&& this[hostName][dataName].setItemLayout(this.dataIndex,
layout, merge);
},
- /**
- * @return {Object}
- */
- getLayout: function () {
+ getLayout: function (this: Host) {
return this[hostName][dataName].getItemLayout(this.dataIndex);
},
- /**
- * @return {module:zrender/Element}
- */
- getGraphicEl: function () {
+ getGraphicEl: function (this: Host): Element {
return this[hostName][dataName].getItemGraphicEl(this.dataIndex);
},
- /**
- * @return {number}
- */
- getRawIndex: function () {
+ getRawIndex: function (this: Host) {
return this[hostName][dataName].getRawIndex(this.dataIndex);
}
};
};
-zrUtil.mixin(Node, createGraphDataProxyMixin('hostGraph', 'data'));
-zrUtil.mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData'));
-Graph.Node = Node;
-Graph.Edge = Edge;
+interface GraphEdge extends ReturnType<typeof createGraphDataProxyMixin> {};
+interface GraphNode extends ReturnType<typeof createGraphDataProxyMixin> {};
+
+zrUtil.mixin(GraphEdge, createGraphDataProxyMixin('hostGraph', 'data'));
+zrUtil.mixin(GraphNode, createGraphDataProxyMixin('hostGraph', 'edgeData'));
+
+Graph.Node = GraphNode;
+Graph.Edge = GraphEdge;
-enableClassCheck(Node);
-enableClassCheck(Edge);
export default Graph;
diff --git a/src/data/Tree.ts b/src/data/Tree.ts
index 61f24cb..1726a5c 100644
--- a/src/data/Tree.ts
+++ b/src/data/Tree.ts
@@ -17,12 +17,8 @@
* under the License.
*/
-// @ts-nocheck
-
/**
* Tree data structure
- *
- * @module echarts/data/Tree
*/
import * as zrUtil from 'zrender/src/core/util';
@@ -30,81 +26,58 @@ import Model from '../model/Model';
import linkList from './helper/linkList';
import List from './List';
import createDimensions from './helper/createDimensions';
+import { DimensionLoose, ParsedDataValue } from '../util/types';
+import { Dictionary } from 'zrender/src/core/types';
+
+type TreeTraverseOrder = 'preorder' | 'postorder';
+type TreeTraverseCallback<Ctx> = (this: Ctx, node: TreeNode) => boolean;
+type TreeTraverseOption = {
+ order?: TreeTraverseOrder
+ attr?: 'children' | 'viewChildren'
+};
-/**
- * @constructor module:echarts/data/Tree~TreeNode
- * @param {string} name
- * @param {module:echarts/data/Tree} hostTree
- */
-var TreeNode = function (name, hostTree) {
- /**
- * @type {string}
- */
- this.name = name || '';
+interface TreeNodeData {
+ name?: string
+ value?: any
+ children?: TreeNodeData[]
+}
- /**
- * Depth of node
- *
- * @type {number}
- * @readOnly
- */
- this.depth = 0;
+class TreeNode {
+ name: string
- /**
- * Height of the subtree rooted at this node.
- * @type {number}
- * @readOnly
- */
- this.height = 0;
+ depth: number = 0
- /**
- * @type {module:echarts/data/Tree~TreeNode}
- * @readOnly
- */
- this.parentNode = null;
+ height: number = 0
+ parentNode: TreeNode
/**
* Reference to list item.
* Do not persistent dataIndex outside,
* besause it may be changed by list.
* If dataIndex -1,
* this node is logical deleted (filtered) in list.
- *
- * @type {Object}
- * @readOnly
*/
- this.dataIndex = -1;
+ dataIndex: number = -1
- /**
- * @type {Array.<module:echarts/data/Tree~TreeNode>}
- * @readOnly
- */
- this.children = [];
+ children: TreeNode[] = []
- /**
- * @type {Array.<module:echarts/data/Tree~TreeNode>}
- * @pubilc
- */
- this.viewChildren = [];
+ viewChildren: TreeNode[] = []
- /**
- * @type {moduel:echarts/data/Tree}
- * @readOnly
- */
- this.hostTree = hostTree;
-};
+ isExpand: boolean = false
-TreeNode.prototype = {
+ readonly hostTree: Tree<Model, any, any> // TODO: TYPE TreeNode use
generic?
- constructor: TreeNode,
+ constructor(name: string, hostTree: Tree<Model, any, any>) {
+ this.name = name || '';
+ this.hostTree = hostTree;
+ }
/**
* The node is removed.
- * @return {boolean} is removed.
*/
- isRemoved: function () {
+ isRemoved(): boolean {
return this.dataIndex < 0;
- },
+ }
/**
* Travel this subtree (include this node).
@@ -117,16 +90,22 @@ TreeNode.prototype = {
* function () { ... }
* ); // postorder
*
- * @param {(Object|string)} options If string, means order.
- * @param {string=} options.order 'preorder' or 'postorder'
- * @param {string=} options.attr 'children' or 'viewChildren'
- * @param {Function} cb If in preorder and return false,
+ * @param options If string, means order.
+ * @param options.order 'preorder' or 'postorder'
+ * @param options.attr 'children' or 'viewChildren'
+ * @param cb If in preorder and return false,
* its subtree will not be visited.
- * @param {Object} [context]
*/
- eachNode: function (options, cb, context) {
+ eachNode<Ctx>(options: TreeTraverseOrder, cb: TreeTraverseCallback<Ctx>,
context?: Ctx): void
+ eachNode<Ctx>(options: TreeTraverseOption, cb: TreeTraverseCallback<Ctx>,
context?: Ctx): void
+ eachNode<Ctx>(cb: TreeTraverseCallback<Ctx>, context?: Ctx): void
+ eachNode<Ctx>(
+ options: TreeTraverseOrder | TreeTraverseOption |
TreeTraverseCallback<Ctx>,
+ cb?: TreeTraverseCallback<Ctx> | Ctx,
+ context?: Ctx
+ ) {
if (typeof options === 'function') {
- context = cb;
+ context = cb as Ctx;
cb = options;
options = null;
}
@@ -136,25 +115,27 @@ TreeNode.prototype = {
options = {order: options};
}
- var order = options.order || 'preorder';
- var children = this[options.attr || 'children'];
+ var order = (options as TreeTraverseOption).order || 'preorder';
+ var children = this[(options as TreeTraverseOption).attr ||
'children'];
var suppressVisitSub;
- order === 'preorder' && (suppressVisitSub = cb.call(context, this));
+ order === 'preorder' && (suppressVisitSub = (cb as
TreeTraverseCallback<Ctx>).call(context as Ctx, this));
for (var i = 0; !suppressVisitSub && i < children.length; i++) {
- children[i].eachNode(options, cb, context);
+ children[i].eachNode(
+ options as TreeTraverseOption,
+ cb as TreeTraverseCallback<Ctx>,
+ context
+ );
}
- order === 'postorder' && cb.call(context, this);
- },
+ order === 'postorder' && (cb as
TreeTraverseCallback<Ctx>).call(context, this);
+ }
/**
* Update depth and height of this subtree.
- *
- * @param {number} depth
*/
- updateDepthAndHeight: function (depth) {
+ updateDepthAndHeight(depth: number) {
var height = 0;
this.depth = depth;
for (var i = 0; i < this.children.length; i++) {
@@ -165,13 +146,9 @@ TreeNode.prototype = {
}
}
this.height = height + 1;
- },
+ }
- /**
- * @param {string} id
- * @return {module:echarts/data/Tree~TreeNode}
- */
- getNodeById: function (id) {
+ getNodeById(id: string): TreeNode {
if (this.getId() === id) {
return this;
}
@@ -181,13 +158,9 @@ TreeNode.prototype = {
return res;
}
}
- },
+ }
- /**
- * @param {module:echarts/data/Tree~TreeNode} node
- * @return {boolean}
- */
- contains: function (node) {
+ contains(node: TreeNode): boolean {
if (node === this) {
return true;
}
@@ -197,13 +170,13 @@ TreeNode.prototype = {
return res;
}
}
- },
+ }
/**
- * @param {boolean} includeSelf Default false.
- * @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root,
child, grandchild, ...]
+ * @param includeSelf Default false.
+ * @return order: [root, child, grandchild, ...]
*/
- getAncestors: function (includeSelf) {
+ getAncestors(includeSelf?: boolean): TreeNode[] {
var ancestors = [];
var node = includeSelf ? this : this.parentNode;
while (node) {
@@ -212,38 +185,27 @@ TreeNode.prototype = {
}
ancestors.reverse();
return ancestors;
- },
+ }
- /**
- * @param {string|Array=} [dimension='value'] Default 'value'. can be 0,
1, 2, 3
- * @return {number} Value.
- */
- getValue: function (dimension) {
+ getValue(dimension: DimensionLoose): ParsedDataValue {
var data = this.hostTree.data;
return data.get(data.getDimension(dimension || 'value'),
this.dataIndex);
- },
+ }
- /**
- * @param {Object} layout
- * @param {boolean=} [merge=false]
- */
- setLayout: function (layout, merge) {
+ setLayout(layout: any, merge?: boolean) {
this.dataIndex >= 0
&& this.hostTree.data.setItemLayout(this.dataIndex, layout, merge);
- },
+ }
/**
* @return {Object} layout
*/
- getLayout: function () {
+ getLayout(): any {
return this.hostTree.data.getItemLayout(this.dataIndex);
- },
+ }
- /**
- * @param {string} [path]
- * @return {module:echarts/model/Model}
- */
- getModel: function (path) {
+ // TODO: TYPE Same type with Model#getModel
+ getModel(path: readonly string[] | string) {
if (this.dataIndex < 0) {
return;
}
@@ -254,22 +216,19 @@ TreeNode.prototype = {
if (!levelModel && (this.children.length === 0 ||
(this.children.length !== 0 && this.isExpand === false))) {
leavesModel = this.getLeavesModel();
}
- return itemModel.getModel(path, (levelModel || leavesModel ||
hostTree.hostModel).getModel(path));
- },
-
- /**
- * @return {module:echarts/model/Model}
- */
- getLevelModel: function () {
+ return itemModel.getModel(
+ path as [string],
+ (levelModel || leavesModel || hostTree.hostModel).getModel(path as
[string])
+ );
+ }
+ // TODO: TYPE More specific model
+ getLevelModel(): Model {
return (this.hostTree.levelModels || [])[this.depth];
- },
+ }
- /**
- * @return {module:echarts/model/Model}
- */
- getLeavesModel: function () {
+ getLeavesModel(): Model {
return this.hostTree.leavesModel;
- },
+ }
/**
* @example
@@ -278,42 +237,35 @@ TreeNode.prototype = {
* 'color': color
* });
*/
- setVisual: function (key, value) {
+ setVisual(key: string, value: any): void
+ setVisual(obj: Dictionary<any>): void
+ setVisual(key: string | Dictionary<any>, value?: any) {
this.dataIndex >= 0
- && this.hostTree.data.setItemVisual(this.dataIndex, key, value);
- },
+ && this.hostTree.data.setItemVisual(this.dataIndex, key as string,
value);
+ }
/**
* Get item visual
*/
- getVisual: function (key, ignoreParent) {
+ getVisual(key: string, ignoreParent?: boolean): any {
return this.hostTree.data.getItemVisual(this.dataIndex, key,
ignoreParent);
- },
+ }
- /**
- * @public
- * @return {number}
- */
- getRawIndex: function () {
+ getRawIndex(): number {
return this.hostTree.data.getRawIndex(this.dataIndex);
- },
+ }
- /**
- * @public
- * @return {string}
- */
- getId: function () {
+ getId(): string {
return this.hostTree.data.getId(this.dataIndex);
- },
+ }
/**
* if this is an ancestor of another node
*
- * @public
- * @param {TreeNode} node another node
- * @return {boolean} if is ancestor
+ * @param node another node
+ * @return if is ancestor
*/
- isAncestorOf: function (node) {
+ isAncestorOf(node: TreeNode): boolean {
var parent = node.parentNode;
while (parent) {
if (parent === this) {
@@ -322,72 +274,46 @@ TreeNode.prototype = {
parent = parent.parentNode;
}
return false;
- },
+ }
/**
* if this is an descendant of another node
*
- * @public
- * @param {TreeNode} node another node
- * @return {boolean} if is descendant
+ * @param node another node
+ * @return if is descendant
*/
- isDescendantOf: function (node) {
+ isDescendantOf(node: TreeNode): boolean {
return node !== this && node.isAncestorOf(this);
}
};
-/**
- * @constructor
- * @alias module:echarts/data/Tree
- * @param {module:echarts/model/Model} hostModel
- * @param {Array.<Object>} levelOptions
- * @param {Object} leavesOption
- */
-function Tree(hostModel, levelOptions, leavesOption) {
- /**
- * @type {module:echarts/data/Tree~TreeNode}
- * @readOnly
- */
- this.root;
+class Tree<HostModel extends Model, LevelOption, LeavesOption> {
- /**
- * @type {module:echarts/data/List}
- * @readOnly
- */
- this.data;
+ type: 'tree' = 'tree'
- /**
- * Index of each item is the same as the raw index of coresponding list
item.
- * @private
- * @type {Array.<module:echarts/data/Tree~TreeNode}
- */
- this._nodes = [];
+ root: TreeNode
- /**
- * @private
- * @readOnly
- * @type {module:echarts/model/Model}
- */
- this.hostModel = hostModel;
+ data: List
- /**
- * @private
- * @readOnly
- * @type {Array.<module:echarts/model/Model}
- */
- this.levelModels = zrUtil.map(levelOptions || [], function (levelDefine) {
- return new Model(levelDefine, hostModel, hostModel.ecModel);
- });
+ hostModel: HostModel
- this.leavesModel = new Model(leavesOption || {}, hostModel,
hostModel.ecModel);
-}
+ levelModels: Model<LevelOption>[]
-Tree.prototype = {
+ leavesModel: Model<LeavesOption>
- constructor: Tree,
+ private _nodes: TreeNode[]
- type: 'tree',
+ constructor(hostModel: HostModel, levelOptions: LevelOption[],
leavesOption: LeavesOption) {
+ this.levelModels = zrUtil.map(levelOptions || [], function
(levelDefine) {
+ return new Model(levelDefine, hostModel, hostModel.ecModel);
+ });
+ this.leavesModel = new Model<LeavesOption>(
+ leavesOption || {} as LeavesOption,
+ hostModel,
+ hostModel.ecModel
+ );
+ }
/**
* Travel this subtree (include this node).
* Usage:
@@ -399,38 +325,37 @@ Tree.prototype = {
* function () { ... }
* ); // postorder
*
- * @param {(Object|string)} options If string, means order.
- * @param {string=} options.order 'preorder' or 'postorder'
- * @param {string=} options.attr 'children' or 'viewChildren'
- * @param {Function} cb
- * @param {Object} [context]
- */
- eachNode: function (options, cb, context) {
- this.root.eachNode(options, cb, context);
- },
+ * @param options If string, means order.
+ * @param options.order 'preorder' or 'postorder'
+ * @param options.attr 'children' or 'viewChildren'
+ * @param cb
+ * @param context
+ */
+ eachNode<Ctx>(options: TreeTraverseOrder, cb: TreeTraverseCallback<Ctx>,
context?: Ctx): void
+ eachNode<Ctx>(options: TreeTraverseOption, cb: TreeTraverseCallback<Ctx>,
context?: Ctx): void
+ eachNode<Ctx>(cb: TreeTraverseCallback<Ctx>, context?: Ctx): void
+ eachNode<Ctx>(
+ options: TreeTraverseOrder | TreeTraverseOption |
TreeTraverseCallback<Ctx>,
+ cb?: TreeTraverseCallback<Ctx> | Ctx,
+ context?: Ctx
+ ) {
+ this.root.eachNode(options as TreeTraverseOption, cb as
TreeTraverseCallback<Ctx>, context);
+ }
- /**
- * @param {number} dataIndex
- * @return {module:echarts/data/Tree~TreeNode}
- */
- getNodeByDataIndex: function (dataIndex) {
+ getNodeByDataIndex(dataIndex: number): TreeNode {
var rawIndex = this.data.getRawIndex(dataIndex);
return this._nodes[rawIndex];
- },
+ }
- /**
- * @param {string} name
- * @return {module:echarts/data/Tree~TreeNode}
- */
- getNodeByName: function (name) {
- return this.root.getNodeByName(name);
- },
+ getNodeById(name: string): TreeNode {
+ return this.root.getNodeById(name);
+ }
/**
* Update item available by list,
* when list has been performed options like 'filterSelf' or 'map'.
*/
- update: function () {
+ update() {
var data = this.data;
var nodes = this._nodes;
@@ -441,98 +366,95 @@ Tree.prototype = {
for (var i = 0, len = data.count(); i < len; i++) {
nodes[data.getRawIndex(i)].dataIndex = i;
}
- },
+ }
/**
* Clear all layouts
*/
- clearLayouts: function () {
+ clearLayouts() {
this.data.clearItemLayouts();
}
-};
-
-/**
- * data node format:
- * {
- * name: ...
- * value: ...
- * children: [
- * {
- * name: ...
- * value: ...
- * children: ...
- * },
- * ...
- * ]
- * }
- *
- * @static
- * @param {Object} dataRoot Root node.
- * @param {module:echarts/model/Model} hostModel
- * @param {Object} treeOptions
- * @param {Array.<Object>} treeOptions.levels
- * @param {Array.<Object>} treeOptions.leaves
- * @return module:echarts/data/Tree
- */
-Tree.createTree = function (dataRoot, hostModel, treeOptions, beforeLink) {
-
- var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves);
- var listData = [];
- var dimMax = 1;
-
- buildHierarchy(dataRoot);
-
- function buildHierarchy(dataNode, parentNode) {
- var value = dataNode.value;
- dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 1);
-
- listData.push(dataNode);
-
- var node = new TreeNode(dataNode.name, tree);
- parentNode
- ? addChild(node, parentNode)
- : (tree.root = node);
- tree._nodes.push(node);
- var children = dataNode.children;
- if (children) {
- for (var i = 0; i < children.length; i++) {
- buildHierarchy(children[i], node);
+ /**
+ * data node format:
+ * {
+ * name: ...
+ * value: ...
+ * children: [
+ * {
+ * name: ...
+ * value: ...
+ * children: ...
+ * },
+ * ...
+ * ]
+ * }
+ */
+ static createTree<T extends TreeNodeData, HostModel extends Model,
LevelOption, LeavesOption>(
+ dataRoot: T,
+ hostModel: HostModel,
+ treeOptions?: {levels: LevelOption[], leaves: LeavesOption},
+ beforeLink?: (data: List) => void
+ ) {
+
+ var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves);
+ var listData: TreeNodeData[] = [];
+ var dimMax = 1;
+
+ buildHierarchy(dataRoot);
+
+ function buildHierarchy(dataNode: TreeNodeData, parentNode?: TreeNode)
{
+ var value = dataNode.value;
+ dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length :
1);
+
+ listData.push(dataNode);
+
+ var node = new TreeNode(dataNode.name, tree);
+ parentNode
+ ? addChild(node, parentNode)
+ : (tree.root = node);
+
+ tree._nodes.push(node);
+
+ var children = dataNode.children;
+ if (children) {
+ for (var i = 0; i < children.length; i++) {
+ buildHierarchy(children[i], node);
+ }
}
}
- }
- tree.root.updateDepthAndHeight(0);
+ tree.root.updateDepthAndHeight(0);
- var dimensionsInfo = createDimensions(listData, {
- coordDimensions: ['value'],
- dimensionsCount: dimMax
- });
+ var dimensionsInfo = createDimensions(listData, {
+ coordDimensions: ['value'],
+ dimensionsCount: dimMax
+ });
- var list = new List(dimensionsInfo, hostModel);
- list.initData(listData);
+ var list = new List(dimensionsInfo, hostModel);
+ list.initData(listData);
- linkList({
- mainData: list,
- struct: tree,
- structAttr: 'tree'
- });
+ linkList({
+ mainData: list,
+ struct: tree,
+ structAttr: 'tree'
+ });
- tree.update();
+ tree.update();
- beforeLink && beforeLink(list);
+ beforeLink && beforeLink(list);
- return tree;
-};
+ return tree;
+ }
+
+}
/**
* It is needed to consider the mess of 'list', 'hostModel' when creating a
TreeNote,
* so this function is not ready and not necessary to be public.
- *
- * @param {(module:echarts/data/Tree~TreeNode|Object)} child
*/
-function addChild(child, node) {
+function addChild(child: TreeNode, node: TreeNode) {
var children = node.children;
if (child.parentNode === node) {
return;
diff --git a/src/data/helper/createDimensions.ts
b/src/data/helper/createDimensions.ts
index ae187f3..8de100f 100644
--- a/src/data/helper/createDimensions.ts
+++ b/src/data/helper/createDimensions.ts
@@ -45,7 +45,8 @@ export type CreateDimensionsParams = {
* @param opt.encodeDefaulter Make default encode if user not specified.
*/
export default function (
- source: Source | List,
+ // TODO: TYPE completeDimensions type
+ source: Source | List | any[],
opt?: CreateDimensionsParams
): DataDimensionInfo[] {
opt = opt || {};
diff --git a/src/model/Model.ts b/src/model/Model.ts
index 6f8480e..4646e3d 100644
--- a/src/model/Model.ts
+++ b/src/model/Model.ts
@@ -248,8 +248,8 @@ function doGet(obj: ModelOption, pathArr: readonly
string[], parentModel?: Model
}
}
if (obj == null && parentModel) {
- // TODO At most 3 items array.
- obj = parentModel.get(pathArr as [string, string, string]);
+ // TODO At most 3 items array. support string[]?
+ obj = parentModel.get(pathArr as [string]);
}
return obj;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]