|  | /* | 
|  | * 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 * as graphic from '../../util/graphic'; | 
|  | import {getECData} from '../../util/innerStore'; | 
|  | import {createTextStyle} from '../../label/labelStyle'; | 
|  | import {getLayoutRect} from '../../util/layout'; | 
|  | import ComponentModel from '../../model/Component'; | 
|  | import { | 
|  | ComponentOption, | 
|  | BoxLayoutOptionMixin, | 
|  | ZRTextAlign, | 
|  | ZRTextVerticalAlign, | 
|  | ZRColor, | 
|  | BorderOptionMixin, | 
|  | LabelOption | 
|  | } from '../../util/types'; | 
|  | import ComponentView from '../../view/Component'; | 
|  | import GlobalModel from '../../model/Global'; | 
|  | import ExtensionAPI from '../../core/ExtensionAPI'; | 
|  | import {windowOpen} from '../../util/format'; | 
|  | import { EChartsExtensionInstallRegisters } from '../../extension'; | 
|  |  | 
|  |  | 
|  | export interface TitleOption extends ComponentOption, BoxLayoutOptionMixin, BorderOptionMixin { | 
|  |  | 
|  | mainType?: 'title' | 
|  |  | 
|  | show?: boolean | 
|  |  | 
|  | text?: string | 
|  | /** | 
|  | * Link to url | 
|  | */ | 
|  | link?: string | 
|  | target?: 'self' | 'blank' | 
|  |  | 
|  | subtext?: string | 
|  | sublink?: string | 
|  | subtarget?: 'self' | 'blank' | 
|  |  | 
|  | textAlign?: ZRTextAlign | 
|  | textVerticalAlign?: ZRTextVerticalAlign | 
|  |  | 
|  | /** | 
|  | * @deprecated Use textVerticalAlign instead | 
|  | */ | 
|  | textBaseline?: ZRTextVerticalAlign | 
|  |  | 
|  | backgroundColor?: ZRColor | 
|  | /** | 
|  | * Padding between text and border. | 
|  | * Support to be a single number or an array. | 
|  | */ | 
|  | padding?: number | number[] | 
|  | /** | 
|  | * Gap between text and subtext | 
|  | */ | 
|  | itemGap?: number | 
|  |  | 
|  | textStyle?: LabelOption | 
|  |  | 
|  | subtextStyle?: LabelOption | 
|  |  | 
|  | /** | 
|  | * If trigger mouse or touch event | 
|  | */ | 
|  | triggerEvent?: boolean | 
|  |  | 
|  | /** | 
|  | * Radius of background border. | 
|  | */ | 
|  | borderRadius?: number | number[] | 
|  | } | 
|  | class TitleModel extends ComponentModel<TitleOption> { | 
|  | static type = 'title' as const; | 
|  | type = TitleModel.type; | 
|  |  | 
|  | readonly layoutMode = {type: 'box', ignoreSize: true} as const; | 
|  |  | 
|  | static defaultOption: TitleOption = { | 
|  | // zlevel: 0, | 
|  | z: 6, | 
|  | show: true, | 
|  |  | 
|  | text: '', | 
|  | target: 'blank', | 
|  | subtext: '', | 
|  |  | 
|  | subtarget: 'blank', | 
|  |  | 
|  | left: 0, | 
|  | top: 0, | 
|  |  | 
|  | backgroundColor: 'rgba(0,0,0,0)', | 
|  |  | 
|  | borderColor: '#ccc', | 
|  |  | 
|  | borderWidth: 0, | 
|  |  | 
|  | padding: 5, | 
|  |  | 
|  | itemGap: 10, | 
|  | textStyle: { | 
|  | fontSize: 18, | 
|  | fontWeight: 'bold', | 
|  | color: '#464646' | 
|  | }, | 
|  | subtextStyle: { | 
|  | fontSize: 12, | 
|  | color: '#6E7079' | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  |  | 
|  | // View | 
|  | class TitleView extends ComponentView { | 
|  |  | 
|  | static type = 'title' as const; | 
|  | type = TitleView.type; | 
|  |  | 
|  |  | 
|  | render(titleModel: TitleModel, ecModel: GlobalModel, api: ExtensionAPI) { | 
|  | this.group.removeAll(); | 
|  |  | 
|  | if (!titleModel.get('show')) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const group = this.group; | 
|  |  | 
|  | const textStyleModel = titleModel.getModel('textStyle'); | 
|  | const subtextStyleModel = titleModel.getModel('subtextStyle'); | 
|  |  | 
|  | let textAlign = titleModel.get('textAlign'); | 
|  | let textVerticalAlign = zrUtil.retrieve2( | 
|  | titleModel.get('textBaseline'), titleModel.get('textVerticalAlign') | 
|  | ); | 
|  |  | 
|  | const textEl = new graphic.Text({ | 
|  | style: createTextStyle(textStyleModel, { | 
|  | text: titleModel.get('text'), | 
|  | fill: textStyleModel.getTextColor() | 
|  | }, {disableBox: true}), | 
|  | z2: 10 | 
|  | }); | 
|  |  | 
|  | const textRect = textEl.getBoundingRect(); | 
|  |  | 
|  | const subText = titleModel.get('subtext'); | 
|  | const subTextEl = new graphic.Text({ | 
|  | style: createTextStyle(subtextStyleModel, { | 
|  | text: subText, | 
|  | fill: subtextStyleModel.getTextColor(), | 
|  | y: textRect.height + titleModel.get('itemGap'), | 
|  | verticalAlign: 'top' | 
|  | }, {disableBox: true}), | 
|  | z2: 10 | 
|  | }); | 
|  |  | 
|  | const link = titleModel.get('link'); | 
|  | const sublink = titleModel.get('sublink'); | 
|  | const triggerEvent = titleModel.get('triggerEvent', true); | 
|  |  | 
|  | textEl.silent = !link && !triggerEvent; | 
|  | subTextEl.silent = !sublink && !triggerEvent; | 
|  |  | 
|  | if (link) { | 
|  | textEl.on('click', function () { | 
|  | windowOpen(link, '_' + titleModel.get('target')); | 
|  | }); | 
|  | } | 
|  | if (sublink) { | 
|  | subTextEl.on('click', function () { | 
|  | windowOpen(sublink, '_' + titleModel.get('subtarget')); | 
|  | }); | 
|  | } | 
|  |  | 
|  | getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent | 
|  | ? { | 
|  | componentType: 'title', | 
|  | componentIndex: titleModel.componentIndex | 
|  | } | 
|  | : null; | 
|  |  | 
|  | group.add(textEl); | 
|  | subText && group.add(subTextEl); | 
|  | // If no subText, but add subTextEl, there will be an empty line. | 
|  |  | 
|  | let groupRect = group.getBoundingRect(); | 
|  | const layoutOption = titleModel.getBoxLayoutParams(); | 
|  | layoutOption.width = groupRect.width; | 
|  | layoutOption.height = groupRect.height; | 
|  | const layoutRect = getLayoutRect( | 
|  | layoutOption, { | 
|  | width: api.getWidth(), | 
|  | height: api.getHeight() | 
|  | }, titleModel.get('padding') | 
|  | ); | 
|  | // Adjust text align based on position | 
|  | if (!textAlign) { | 
|  | // Align left if title is on the left. center and right is same | 
|  | textAlign = (titleModel.get('left') || titleModel.get('right')) as ZRTextAlign; | 
|  | // @ts-ignore | 
|  | if (textAlign === 'middle') { | 
|  | textAlign = 'center'; | 
|  | } | 
|  | // Adjust layout by text align | 
|  | if (textAlign === 'right') { | 
|  | layoutRect.x += layoutRect.width; | 
|  | } | 
|  | else if (textAlign === 'center') { | 
|  | layoutRect.x += layoutRect.width / 2; | 
|  | } | 
|  | } | 
|  | if (!textVerticalAlign) { | 
|  | textVerticalAlign = (titleModel.get('top') || titleModel.get('bottom')) as ZRTextVerticalAlign; | 
|  | // @ts-ignore | 
|  | if (textVerticalAlign === 'center') { | 
|  | textVerticalAlign = 'middle'; | 
|  | } | 
|  | if (textVerticalAlign === 'bottom') { | 
|  | layoutRect.y += layoutRect.height; | 
|  | } | 
|  | else if (textVerticalAlign === 'middle') { | 
|  | layoutRect.y += layoutRect.height / 2; | 
|  | } | 
|  |  | 
|  | textVerticalAlign = textVerticalAlign || 'top'; | 
|  | } | 
|  |  | 
|  | group.x = layoutRect.x; | 
|  | group.y = layoutRect.y; | 
|  | group.markRedraw(); | 
|  | const alignStyle = { | 
|  | align: textAlign, | 
|  | verticalAlign: textVerticalAlign | 
|  | }; | 
|  | textEl.setStyle(alignStyle); | 
|  | subTextEl.setStyle(alignStyle); | 
|  |  | 
|  | // Render background | 
|  | // Get groupRect again because textAlign has been changed | 
|  | groupRect = group.getBoundingRect(); | 
|  | const padding = layoutRect.margin; | 
|  | const style = titleModel.getItemStyle(['color', 'opacity']); | 
|  | style.fill = titleModel.get('backgroundColor'); | 
|  | const rect = new graphic.Rect({ | 
|  | shape: { | 
|  | x: groupRect.x - padding[3], | 
|  | y: groupRect.y - padding[0], | 
|  | width: groupRect.width + padding[1] + padding[3], | 
|  | height: groupRect.height + padding[0] + padding[2], | 
|  | r: titleModel.get('borderRadius') | 
|  | }, | 
|  | style: style, | 
|  | subPixelOptimize: true, | 
|  | silent: true | 
|  | }); | 
|  |  | 
|  | group.add(rect); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | export function install(registers: EChartsExtensionInstallRegisters) { | 
|  | registers.registerComponentModel(TitleModel); | 
|  | registers.registerComponentView(TitleView); | 
|  | } |