| /* | 
 | * 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 ExtensionAPI from '../../core/ExtensionAPI'; | 
 | import { ZRenderType } from 'zrender/src/zrender'; | 
 | import { TooltipOption } from './TooltipModel'; | 
 | import { ZRColor } from '../../util/types'; | 
 | import Model from '../../model/Model'; | 
 | import ZRText, { TextStyleProps } from 'zrender/src/graphic/Text'; | 
 | import { TooltipMarkupStyleCreator, getPaddingFromTooltipModel } from './tooltipMarkup'; | 
 | import { throwError } from '../../util/log'; | 
 |  | 
 | class TooltipRichContent { | 
 |  | 
 |     private _zr: ZRenderType; | 
 |  | 
 |     private _show = false; | 
 |  | 
 |     private _styleCoord: [number, number, number, number] = [0, 0, 0, 0]; | 
 |  | 
 |     private _hideTimeout: number; | 
 |  | 
 |     private _enterable = true; | 
 |  | 
 |     private _inContent: boolean; | 
 |  | 
 |     private _hideDelay: number; | 
 |  | 
 |     el: ZRText; | 
 |  | 
 |     constructor(api: ExtensionAPI) { | 
 |         this._zr = api.getZr(); | 
 |         makeStyleCoord(this._styleCoord, this._zr, api.getWidth() / 2, api.getHeight() / 2); | 
 |     } | 
 |  | 
 |     /** | 
 |      * Update when tooltip is rendered | 
 |      */ | 
 |     update(tooltipModel: Model<TooltipOption>) { | 
 |         const alwaysShowContent = tooltipModel.get('alwaysShowContent'); | 
 |         alwaysShowContent && this._moveIfResized(); | 
 |     } | 
 |  | 
 |     show() { | 
 |         if (this._hideTimeout) { | 
 |             clearTimeout(this._hideTimeout); | 
 |         } | 
 |  | 
 |         this.el.show(); | 
 |         this._show = true; | 
 |     } | 
 |  | 
 |     /** | 
 |      * Set tooltip content | 
 |      */ | 
 |     setContent( | 
 |         content: string | HTMLElement | HTMLElement[], | 
 |         markupStyleCreator: TooltipMarkupStyleCreator, | 
 |         tooltipModel: Model<TooltipOption>, | 
 |         borderColor: ZRColor, | 
 |         arrowPosition: TooltipOption['position'] | 
 |     ) { | 
 |         if (zrUtil.isObject(content)) { | 
 |             throwError(__DEV__ ? 'Passing DOM nodes as content is not supported in richText tooltip!' : ''); | 
 |         } | 
 |         if (this.el) { | 
 |             this._zr.remove(this.el); | 
 |         } | 
 |  | 
 |         const textStyleModel = tooltipModel.getModel('textStyle'); | 
 |  | 
 |         this.el = new ZRText({ | 
 |             style: { | 
 |                 rich: markupStyleCreator.richTextStyles, | 
 |                 text: content as string, | 
 |                 lineHeight: 22, | 
 |                 borderWidth: 1, | 
 |                 borderColor: borderColor as string, | 
 |                 textShadowColor: textStyleModel.get('textShadowColor'), | 
 |                 fill: tooltipModel.get(['textStyle', 'color']), | 
 |                 padding: getPaddingFromTooltipModel(tooltipModel, 'richText'), | 
 |                 verticalAlign: 'top', | 
 |                 align: 'left' | 
 |             }, | 
 |             z: tooltipModel.get('z') | 
 |         }); | 
 |         zrUtil.each([ | 
 |             'backgroundColor', 'borderRadius', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY' | 
 |         ] as const, propName => { | 
 |             (this.el.style as any)[propName] = tooltipModel.get(propName); | 
 |         }); | 
 |         zrUtil.each([ | 
 |             'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY' | 
 |         ] as const, propName => { | 
 |             this.el.style[propName] = textStyleModel.get(propName) || 0; | 
 |         }); | 
 |  | 
 |         this._zr.add(this.el); | 
 |  | 
 |         const self = this; | 
 |         this.el.on('mouseover', function () { | 
 |             // clear the timeout in hideLater and keep showing tooltip | 
 |             if (self._enterable) { | 
 |                 clearTimeout(self._hideTimeout); | 
 |                 self._show = true; | 
 |             } | 
 |             self._inContent = true; | 
 |         }); | 
 |         this.el.on('mouseout', function () { | 
 |             if (self._enterable) { | 
 |                 if (self._show) { | 
 |                     self.hideLater(self._hideDelay); | 
 |                 } | 
 |             } | 
 |             self._inContent = false; | 
 |         }); | 
 |     } | 
 |  | 
 |     setEnterable(enterable?: boolean) { | 
 |         this._enterable = enterable; | 
 |     } | 
 |  | 
 |     getSize() { | 
 |         const el = this.el; | 
 |         const bounding = this.el.getBoundingRect(); | 
 |         // bounding rect does not include shadow. For renderMode richText, | 
 |         // if overflow, it will be cut. So calculate them accurately. | 
 |         const shadowOuterSize = calcShadowOuterSize(el.style); | 
 |         return [ | 
 |             bounding.width + shadowOuterSize.left + shadowOuterSize.right, | 
 |             bounding.height + shadowOuterSize.top + shadowOuterSize.bottom | 
 |         ]; | 
 |     } | 
 |  | 
 |     moveTo(x: number, y: number) { | 
 |         const el = this.el; | 
 |         if (el) { | 
 |             const styleCoord = this._styleCoord; | 
 |             makeStyleCoord(styleCoord, this._zr, x, y); | 
 |             x = styleCoord[0]; | 
 |             y = styleCoord[1]; | 
 |             const style = el.style; | 
 |             const borderWidth = mathMaxWith0(style.borderWidth || 0); | 
 |             const shadowOuterSize = calcShadowOuterSize(style); | 
 |             // rich text x, y do not include border. | 
 |             el.x = x + borderWidth + shadowOuterSize.left; | 
 |             el.y = y + borderWidth + shadowOuterSize.top; | 
 |             el.markRedraw(); | 
 |         } | 
 |     } | 
 |  | 
 |  | 
 |     /** | 
 |      * when `alwaysShowContent` is true, | 
 |      * move the tooltip after chart resized | 
 |      */ | 
 |     _moveIfResized() { | 
 |         // The ratio of left to width | 
 |         const ratioX = this._styleCoord[2]; | 
 |         // The ratio of top to height | 
 |         const ratioY = this._styleCoord[3]; | 
 |         this.moveTo( | 
 |             ratioX * this._zr.getWidth(), | 
 |             ratioY * this._zr.getHeight() | 
 |         ); | 
 |     } | 
 |  | 
 |     hide() { | 
 |         if (this.el) { | 
 |             this.el.hide(); | 
 |         } | 
 |         this._show = false; | 
 |     } | 
 |  | 
 |     hideLater(time?: number) { | 
 |         if (this._show && !(this._inContent && this._enterable)) { | 
 |             if (time) { | 
 |                 this._hideDelay = time; | 
 |                 // Set show false to avoid invoke hideLater multiple times | 
 |                 this._show = false; | 
 |                 this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time) as any; | 
 |             } | 
 |             else { | 
 |                 this.hide(); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     isShow() { | 
 |         return this._show; | 
 |     } | 
 |  | 
 |     dispose() { | 
 |         this._zr.remove(this.el); | 
 |     } | 
 | } | 
 |  | 
 | function mathMaxWith0(val: number): number { | 
 |     return Math.max(0, val); | 
 | } | 
 |  | 
 | function calcShadowOuterSize(style: TextStyleProps) { | 
 |     const shadowBlur = mathMaxWith0(style.shadowBlur || 0); | 
 |     const shadowOffsetX = mathMaxWith0(style.shadowOffsetX || 0); | 
 |     const shadowOffsetY = mathMaxWith0(style.shadowOffsetY || 0); | 
 |     return { | 
 |         left: mathMaxWith0(shadowBlur - shadowOffsetX), | 
 |         right: mathMaxWith0(shadowBlur + shadowOffsetX), | 
 |         top: mathMaxWith0(shadowBlur - shadowOffsetY), | 
 |         bottom: mathMaxWith0(shadowBlur + shadowOffsetY) | 
 |     }; | 
 | } | 
 |  | 
 | function makeStyleCoord(out: number[], zr: ZRenderType, zrX: number, zrY: number) { | 
 |     out[0] = zrX; | 
 |     out[1] = zrY; | 
 |     out[2] = out[0] / zr.getWidth(); | 
 |     out[3] = out[1] / zr.getHeight(); | 
 | } | 
 |  | 
 | export default TooltipRichContent; |