|  | /* | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | import * as zrUtil from 'zrender/src/core/util'; | 
|  | import ChartView from '../../view/Chart'; | 
|  | import SunburstPiece from './SunburstPiece'; | 
|  | import DataDiffer from '../../data/DataDiffer'; | 
|  | import SunburstSeriesModel, { SunburstSeriesNodeItemOption } from './SunburstSeries'; | 
|  | import GlobalModel from '../../model/Global'; | 
|  | import ExtensionAPI from '../../core/ExtensionAPI'; | 
|  | import { TreeNode } from '../../data/Tree'; | 
|  | import { ROOT_TO_NODE_ACTION } from './sunburstAction'; | 
|  | import { windowOpen } from '../../util/format'; | 
|  |  | 
|  | interface DrawTreeNode extends TreeNode { | 
|  | parentNode: DrawTreeNode | 
|  | piece: SunburstPiece | 
|  | children: DrawTreeNode[] | 
|  | } | 
|  | class SunburstView extends ChartView { | 
|  |  | 
|  | static readonly type = 'sunburst'; | 
|  | readonly type = SunburstView.type; | 
|  |  | 
|  | seriesModel: SunburstSeriesModel; | 
|  | api: ExtensionAPI; | 
|  | ecModel: GlobalModel; | 
|  |  | 
|  | virtualPiece: SunburstPiece; | 
|  |  | 
|  | private _oldChildren: DrawTreeNode[]; | 
|  |  | 
|  | render( | 
|  | seriesModel: SunburstSeriesModel, | 
|  | ecModel: GlobalModel, | 
|  | api: ExtensionAPI, | 
|  | // @ts-ignore | 
|  | payload | 
|  | ) { | 
|  | const self = this; | 
|  |  | 
|  | this.seriesModel = seriesModel; | 
|  | this.api = api; | 
|  | this.ecModel = ecModel; | 
|  |  | 
|  | const data = seriesModel.getData(); | 
|  | const virtualRoot = data.tree.root as DrawTreeNode; | 
|  |  | 
|  | const newRoot = seriesModel.getViewRoot() as DrawTreeNode; | 
|  |  | 
|  | const group = this.group; | 
|  |  | 
|  | const renderLabelForZeroData = seriesModel.get('renderLabelForZeroData'); | 
|  |  | 
|  | const newChildren: DrawTreeNode[] = []; | 
|  | newRoot.eachNode(function (node: DrawTreeNode) { | 
|  | newChildren.push(node); | 
|  | }); | 
|  | const oldChildren = this._oldChildren || []; | 
|  |  | 
|  | dualTravel(newChildren, oldChildren); | 
|  |  | 
|  | renderRollUp(virtualRoot, newRoot); | 
|  |  | 
|  | this._initEvents(); | 
|  |  | 
|  | this._oldChildren = newChildren; | 
|  |  | 
|  | function dualTravel(newChildren: DrawTreeNode[], oldChildren: DrawTreeNode[]) { | 
|  | if (newChildren.length === 0 && oldChildren.length === 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | new DataDiffer(oldChildren, newChildren, getKey, getKey) | 
|  | .add(processNode) | 
|  | .update(processNode) | 
|  | .remove(zrUtil.curry(processNode, null)) | 
|  | .execute(); | 
|  |  | 
|  | function getKey(node: DrawTreeNode) { | 
|  | return node.getId(); | 
|  | } | 
|  |  | 
|  | function processNode(newIdx: number, oldIdx?: number) { | 
|  | const newNode = newIdx == null ? null : newChildren[newIdx]; | 
|  | const oldNode = oldIdx == null ? null : oldChildren[oldIdx]; | 
|  |  | 
|  | doRenderNode(newNode, oldNode); | 
|  | } | 
|  | } | 
|  |  | 
|  | function doRenderNode(newNode: DrawTreeNode, oldNode: DrawTreeNode) { | 
|  | if (!renderLabelForZeroData && newNode && !newNode.getValue()) { | 
|  | // Not render data with value 0 | 
|  | newNode = null; | 
|  | } | 
|  |  | 
|  | if (newNode !== virtualRoot && oldNode !== virtualRoot) { | 
|  | if (oldNode && oldNode.piece) { | 
|  | if (newNode) { | 
|  | // Update | 
|  | oldNode.piece.updateData( | 
|  | false, newNode, seriesModel, ecModel, api | 
|  | ); | 
|  |  | 
|  | // For tooltip | 
|  | data.setItemGraphicEl(newNode.dataIndex, oldNode.piece); | 
|  | } | 
|  | else { | 
|  | // Remove | 
|  | removeNode(oldNode); | 
|  | } | 
|  | } | 
|  | else if (newNode) { | 
|  | // Add | 
|  | const piece = new SunburstPiece( | 
|  | newNode, | 
|  | seriesModel, | 
|  | ecModel, | 
|  | api | 
|  | ); | 
|  | group.add(piece); | 
|  |  | 
|  | // For tooltip | 
|  | data.setItemGraphicEl(newNode.dataIndex, piece); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | function removeNode(node: DrawTreeNode) { | 
|  | if (!node) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (node.piece) { | 
|  | group.remove(node.piece); | 
|  | node.piece = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | function renderRollUp(virtualRoot: DrawTreeNode, viewRoot: DrawTreeNode) { | 
|  | if (viewRoot.depth > 0) { | 
|  | // Render | 
|  | if (self.virtualPiece) { | 
|  | // Update | 
|  | self.virtualPiece.updateData( | 
|  | false, virtualRoot, seriesModel, ecModel, api | 
|  | ); | 
|  | } | 
|  | else { | 
|  | // Add | 
|  | self.virtualPiece = new SunburstPiece( | 
|  | virtualRoot, | 
|  | seriesModel, | 
|  | ecModel, | 
|  | api | 
|  | ); | 
|  | group.add(self.virtualPiece); | 
|  | } | 
|  |  | 
|  | // TODO event scope | 
|  | viewRoot.piece.off('click'); | 
|  | self.virtualPiece.on('click', function (e) { | 
|  | self._rootToNode(viewRoot.parentNode); | 
|  | }); | 
|  | } | 
|  | else if (self.virtualPiece) { | 
|  | // Remove | 
|  | group.remove(self.virtualPiece); | 
|  | self.virtualPiece = null; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @private | 
|  | */ | 
|  | _initEvents() { | 
|  | this.group.off('click'); | 
|  | this.group.on('click', (e) => { | 
|  | let targetFound = false; | 
|  | const viewRoot = this.seriesModel.getViewRoot(); | 
|  | viewRoot.eachNode((node: DrawTreeNode) => { | 
|  | if (!targetFound | 
|  | && node.piece && node.piece === e.target | 
|  | ) { | 
|  | const nodeClick = node.getModel<SunburstSeriesNodeItemOption>().get('nodeClick'); | 
|  | if (nodeClick === 'rootToNode') { | 
|  | this._rootToNode(node); | 
|  | } | 
|  | else if (nodeClick === 'link') { | 
|  | const itemModel = node.getModel<SunburstSeriesNodeItemOption>(); | 
|  | const link = itemModel.get('link'); | 
|  | if (link) { | 
|  | const linkTarget = itemModel.get('target', true) | 
|  | || '_blank'; | 
|  | windowOpen(link, linkTarget); | 
|  | } | 
|  | } | 
|  | targetFound = true; | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @private | 
|  | */ | 
|  | _rootToNode(node: DrawTreeNode) { | 
|  | if (node !== this.seriesModel.getViewRoot()) { | 
|  | this.api.dispatchAction({ | 
|  | type: ROOT_TO_NODE_ACTION, | 
|  | from: this.uid, | 
|  | seriesId: this.seriesModel.id, | 
|  | targetNode: node | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @implement | 
|  | */ | 
|  | containPoint(point: number[], seriesModel: SunburstSeriesModel) { | 
|  | const treeRoot = seriesModel.getData(); | 
|  | const itemLayout = treeRoot.getItemLayout(0); | 
|  | if (itemLayout) { | 
|  | const dx = point[0] - itemLayout.cx; | 
|  | const dy = point[1] - itemLayout.cy; | 
|  | const radius = Math.sqrt(dx * dx + dy * dy); | 
|  | return radius <= itemLayout.r && radius >= itemLayout.r0; | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | export default SunburstView; |