| /* | 
 | * 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; |